DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
src/hardware/dongle.cpp
00001 #include <string.h>
00002 #include "dosbox.h"
00003 #include "inout.h"
00004 #include "pic.h"
00005 #include "setup.h"
00006 #include "control.h"
00007 
00008 /*
00009 
00010 This is DosBox handler of 93c46 copy-protection dongle connected to LPT port.
00011 At least Rainbow Sentinel Cplus and MicroPhar are 93c46-based dongles.
00012 
00013 93c46 memory chip contain 64*16 words. More on it:
00014 http://www.atmel.com/dyn/resources/prod_documents/doc0172.pdf
00015 
00016 Few notes:
00017 
00018 * It is unable to detect dongle presence on software side, so, software
00019 usually reading some cell (often 0x3F) and check magic value.
00020 
00021 * Wiring scheme may differ from dongle to dongle, but usually, 
00022 DI (data input), SK (clock), CS (chip select) and power lines are 
00023 taken from D0..D7 in some order.
00024 
00025 * DO (data output) may be connected to ACK or BUSY printer lines.
00026 
00027 Add this file to DosBox project, patch dosbox.cpp patch and add to dosbox.conf
00028 "dongle=true" under "[speaker]" section.
00029 
00030 More information: http://blogs.conus.info/node/56
00031 
00032 -- dennis@conus.info
00033 
00034 */
00035 
00036 #define DONGLE_BASE             0x0378
00037 
00038 #define IS_SET(flag, bit)       ((flag) & (bit))
00039 
00040 static bool queue_filling=false;
00041 static bool queue_filled=false;
00042 static int queue_idx=0;
00043 static int queue[3+6];
00044 static int out_idx;
00045 
00046 static int last_SK=0;
00047 static bool ackbit=false;
00048 
00049 static int ADR;
00050 
00051 static unsigned short MEMORY[0x40]=
00052 { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     // 0
00053   0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     // 8
00054   0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     // 10
00055   0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     // 18
00056   0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     // 20
00057   0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     // 28
00058   0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,     // 30
00059   0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 };   // 38
00060 
00061 //#include <windows.h>
00062 
00063 static void dongle_write(Bitu port,Bitu val,Bitu iolen) {
00064     (void)iolen;//UNUSED
00065     (void)port;//UNUSED
00066         static int DI, SK;
00067         /*
00068         LOG(LOG_MISC,LOG_NORMAL)("write dongle port=%x val=%d%d%d%d%d%d%d%d\n",
00069         port,
00070         IS_SET (val, 1<<7) ? 1 : 0,
00071         IS_SET (val, 1<<6) ? 1 : 0,
00072         IS_SET (val, 1<<5) ? 1 : 0,
00073         IS_SET (val, 1<<4) ? 1 : 0,
00074         IS_SET (val, 1<<3) ? 1 : 0,
00075         IS_SET (val, 1<<2) ? 1 : 0,
00076         IS_SET (val, 1<<1) ? 1 : 0,
00077         IS_SET (val, 1<<0) ? 1 : 0);
00078         */
00079         DI=IS_SET (val, 1<<7) ? 1 : 0;
00080         SK=IS_SET (val, 1<<6) ? 1 : 0;
00081 
00082         if (last_SK==0 && SK==1) // posedge
00083         {
00084                 if (queue_filled)
00085                 {
00086                         if (((MEMORY[ADR]>>out_idx)&1)==1)
00087                                 ackbit=false; // swap it if ACK is negated...
00088                         else
00089                                 ackbit=true;
00090 
00091                         if (out_idx==0)
00092                                 queue_filled=false;
00093                         else
00094                                 out_idx--;
00095                 };
00096 
00097                 if (queue_filling==false && DI==1) // start bit
00098                 {                                
00099                         //got start bit
00100                         queue_filling=true;
00101                         queue_filled=false;
00102                         queue_idx=0;
00103                 };
00104 
00105                 if (queue_filling)
00106                 {
00107                         queue[queue_idx]=DI;
00108                         if (queue_idx==8)
00109                         { // last bit filled
00110 
00111                                 int OP1=queue[1];
00112                                 int OP2=queue[2];
00113 
00114                                 ADR=(queue[3]<<5) | (queue[4]<<4) | (queue[5]<<3) | (queue[6]<<2) | (queue[7]<<1) | (queue[8]);
00115 
00116                                 if (OP1==1 && OP2==0) // read
00117                                 {
00118                                         LOG(LOG_MISC,LOG_NORMAL)("93c46 dongle: trying to read at address 0x%x", ADR);
00119 
00120                                         queue_filling=false;
00121                                         queue_filled=true;
00122                                         out_idx=15;
00123                                 }
00124                                 else
00125                                         LOG(LOG_MISC,LOG_NORMAL)("93c46 dongle: OP1=%d; OP2=%d: this command is not handled yet", OP1, OP2);
00126                         }
00127                         else
00128                                 queue_idx++;
00129                 };
00130         };
00131 
00132         last_SK=SK;
00133 }
00134 
00135 static Bitu dongle_read(Bitu port,Bitu iolen) {
00136     (void)iolen;//UNUSED
00137     (void)port;//UNUSED
00138         Bitu retval;
00139         switch (port-DONGLE_BASE) 
00140         {
00141         case 0:         /* Data Port */
00142                 return 0;
00143                 break;
00144 
00145         case 1:         /* Status Port */       
00146                 if (ackbit==true)
00147                         retval=0x40;
00148                 else
00149                         retval=0;
00150 
00151                 return retval;
00152                 break;
00153 
00154         case 2:         /* Control Port */
00155                 return 0;
00156                 break;
00157         }
00158         return 0xff;
00159 }
00160 
00161 class DONGLE: public Module_base {
00162 private:
00163         IO_ReadHandleObject ReadHandler;
00164         IO_WriteHandleObject WriteHandler;
00165 public:
00166         DONGLE(Section* configuration):Module_base(configuration) 
00167         {
00168                 Section_prop * section=static_cast<Section_prop *>(configuration);
00169                 if(!section->Get_bool("dongle")) return;
00170 
00171                 WriteHandler.Install(DONGLE_BASE,dongle_write,IO_MB,3);
00172                 ReadHandler.Install(DONGLE_BASE,dongle_read,IO_MB,3);
00173         }
00174         ~DONGLE(){
00175         }
00176 };
00177 
00178 static DONGLE* test = NULL;
00179 
00180 static void DONGLE_ShutDown(Section* sec){
00181     (void)sec;//UNUSED
00182     if (test) {
00183         delete test;
00184         test = NULL;
00185     }
00186 }
00187 
00188 void DONGLE_OnReset(Section* sec) {
00189     (void)sec;//UNUSED
00190         if (test == NULL && !IS_PC98_ARCH) {
00191                 LOG(LOG_MISC,LOG_DEBUG)("Allocating parallel dongle emulation");
00192                 test = new DONGLE(control->GetSection("parallel"));
00193         }
00194 }
00195 
00196 void DONGLE_Init() {
00197         LOG(LOG_MISC,LOG_DEBUG)("Initializing dongle emulation");
00198 
00199         AddExitFunction(AddExitFunctionFuncPair(DONGLE_ShutDown),true);
00200         AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(DONGLE_OnReset));
00201 }