]> www.vanbest.org Git - sasc-ng.git/commitdiff
nagra: a bunch of updates, mainly map related (emunation)
authorleslie <unknown>
Sat, 25 Jul 2009 07:38:36 +0000 (15:38 +0800)
committerleslie <unknown>
Sat, 25 Jul 2009 07:38:36 +0000 (15:38 +0800)
systems/nagra/cpu.c
systems/nagra/cpu.h
systems/nagra/nagra2-0101.c
systems/nagra/nagra2-0501.c
systems/nagra/nagra2.c
systems/nagra/nagra2.h

index 5afd70e8e4ae24fb0b7896bce695d14b1aa00df4..05c30445ccd60931d94a06a81498c10f6ee0a1b8 100644 (file)
@@ -140,15 +140,16 @@ void cMapEeprom::Set(unsigned short ea, unsigned char val)
 
 #define PAGEOFF(ea,s) (((ea)&0x8000) ? pageMap[s]:0)
 
-c6805::c6805(void) {
+c6805::c6805(void)
+{
   cc.c=0; cc.z=0; cc.n=0; cc.i=0; cc.h=0; cc.v=1;
   pc=0; a=0; x=0; y=0; cr=dr=0; sp=spHi=0x100; spLow=0xC0;
   hasReadHandler=hasWriteHandler=false;
   exptBase=0x4000;
+  ResetCycles();
   ClearExceptions();
   ClearBreakpoints();
   InitMapper();
-  ResetCycles();
   memset(stats,0,sizeof(stats));
   loglb=new cLineBuff(128);
 }
@@ -324,7 +325,7 @@ void c6805::RaiseException(int num)
 
 void c6805::ClearExceptions(void)
 {
-  exptPending=false; timerDisable=0;
+  exptPending=false; exptReady=true; timerDisable=0;
   for(int i=0; i<EXPT_MAX; i++) expt[i]=false;
 }
 
@@ -430,7 +431,7 @@ int c6805::Run(int max_count)
 
   int count=0;
   while (1) {
-    if(exptPending && !cc.i) {
+    if(exptPending && exptReady) {
       exptPending=false;
       for(int i=0; i<EXPT_MAX; ++i)
         if(expt[i]) {
@@ -438,9 +439,11 @@ int c6805::Run(int max_count)
           expt[i]=false;
           pushpc(); push(x); push(a); pushc(); cc.i=1;
           pc=exptBase+4*i;
+          timerDisable=0;
           break;
           }
       }
+    exptReady=!cc.i;
     Stepper();
     if(sp<spLow) {
       PRINTF(L_SYS_EMU,"stack overflow (count=%d)",count);
@@ -489,8 +492,10 @@ int c6805::Run(int max_count)
           case 0x22: case 0x23: case 0x24: case 0x25:
           case 0x26: case 0x27:
             vbra=true; break;
-          case 0x75:
           case 0x8D:
+            cycles++;
+            // fall through
+          case 0x75:
           case 0xC0: case 0xC1: case 0xC2: case 0xC3:
           case 0xC4: case 0xC5: case 0xC6: case 0xC7:
           case 0xC8: case 0xC9: case 0xCA: case 0xCB:
index 0a6f17fce0f4301fd7fbaeb11531a540ca737991..bb51a88cd3304c04c88d665466b4414848b4351f 100644 (file)
@@ -109,7 +109,7 @@ private:
   int pageMap[256];
   bool indirect;
   unsigned int clockcycles;
-  bool exptPending, expt[EXPT_MAX];
+  bool exptPending, exptReady, expt[EXPT_MAX];
   int timerDisable;
   //
   void InitMapper(void);
index 9bc6cdbc2883f60e1b32b7011305ecc4abb84544..9a52ec00c853d7f72465dc959d4ea5f9333ec085 100644 (file)
@@ -125,7 +125,7 @@ private:
   cAuxSrv aux;
 #endif
   //
-  void MakePrime(BIGNUM *n, unsigned char *residues);
+  void MakePrime(unsigned char *residues, bool strong);
 protected:
   virtual bool Map(int f, unsigned char *data, int l);
   };
@@ -157,13 +157,27 @@ const int cMap0101::tim3b[][17] = {
   { 14668,15091,15519,15947,16370,16798,17221,17654,18082,18505,18933,19356,19784,20212,20640,21068,21491 },
   };
 
-void cMap0101::MakePrime(BIGNUM *n, unsigned char *residues)
+void cMap0101::MakePrime(unsigned char *residues, bool strong)
 {
   bool isPrime;
-  cycles+=1290;
+  AddMapCycles(strong?462:258);
+  BN_copy(D,B);
+  AddMapCycles(240);
+  BN_zero(A);
+  AddMapCycles(41);
+  BN_set_word(A,2);
+  A.SetPos(1);
+  AddMapCycles(12);
+  for(int i=2; i<=8; i++) { A.SetPos(i); AddMapCycles(9); }
+  A.SetPos(0);
+  AddMapCycles(8);
+  if(strong) D.GetLE(residues+53,8);
+  unsigned int counts[3] = { 0 };
+  bool first=true;
   do {
-    cycles+=1465;
-    BN_add_word(n,2);
+    counts[0]++;;
+    BN_add_word(B,2);
+    if(first) { first=false; AddMapCycles(1600); }
     isPrime=true;
     for(int i=0; i<53; i++) {
       residues[i]+=2;
@@ -171,12 +185,13 @@ void cMap0101::MakePrime(BIGNUM *n, unsigned char *residues)
       unsigned char denom=primes[i];
       if(num>denom) {
         unsigned char r=0;
-        while(denom>=r) { cycles+=1; r=(r<<1)|((num&0x80)>>7); num<<=1; }
+        while(denom>=r) { counts[1]++; r=(r<<1)|((num&0x80)>>7); num<<=1; }
         }
       residues[i]%=primes[i];
-      if(residues[i]==0) { cycles+=13; isPrime=false; }
+      if(residues[i]==0) { counts[2]++; isPrime=false; }
       }
     } while(!isPrime);
+  cycles=1290+1465*counts[0]+counts[1]+13*counts[2];
 }
 
 bool cMap0101::Map(int f, unsigned char *data, int l)
@@ -190,46 +205,21 @@ bool cMap0101::Map(int f, unsigned char *data, int l)
       cycles=898;
       break;
     case 0x22:
-// START INCOMPLETE FIX
-      BN_zero(B);
-      BN_set_bit(B,80);
-      AddMapCycles(644);
-      BN_zero(B);
-      BN_set_bit(B,96);
-      AddMapCycles(76);
-// END INCOMPLETE FIX
       if(BN_is_zero(D)) { cycles=639-6; break; }
       l&=0x1fff;
+      AddMapCycles(376);
+      BN_zero(B);
+      AddMapCycles(l>=(BN_num_bits(D)-1)/64*64 ? 244 : 210);
       BN_one(B);
+      B.SetPos(1);
+      AddMapCycles(17);
+      for(int i=2; i<=8; i++) { B.SetPos(i); AddMapCycles(9); }
+      AddMapCycles(44);
       BN_mod_lshift(B,B,l,D,ctx);
-      if(l<64)
-        cycles=927+(l&7)*9;
-      else {
-        div_t val=div(l-64,2046);
-        int j=16*((val.rem+1)/2) + (val.rem>4?val.rem-7:val.rem-12)/4;
-        cycles=1086 + j - ((j-2)%5) + 16923*val.quot - ((4*val.quot-2)%5);
-        }
       break;
     case 0x23:
-// START FIX
-      {
-      cBN s,x,y;
-      BN_copy(s,D);
-      BN_rshift(s,s,64+ 1 *8);
-      BN_lshift(s,s,64);
-      BN_copy(x,D);
-      BN_mask_bits(x,64);
-      BN_copy(y,D);
-      BN_rshift(y,y,64);
-      BN_mask_bits(y,1*8);
-      BN_lshift(y,y,128-1*8);
-      BN_copy(D,s);
-      BN_add(D,D,x);
-      BN_add(D,D,y);
-      }
-      BN_zero(B);
-      BN_set_bit(B,88);
-// END FIX
+      AddMapCycles(169);
+      IMonInit0();
       break;
     case 0x25:
       AddMapCycles(254);
@@ -243,8 +233,8 @@ bool cMap0101::Map(int f, unsigned char *data, int l)
       {
       BN_add(B,B,C);
       bool b=BN_is_bit_set(B,wordsize<<6);
-      if(data) data[0]=b;
       if(b) BN_mask_bits(B,wordsize<<6);
+      if(data) data[0]=b;
       cycles=501+(8*wordsize+3)/5*5-6;
       break; 
       }
@@ -256,45 +246,51 @@ bool cMap0101::Map(int f, unsigned char *data, int l)
       break;
       }
     case 0x2e:
-// START INCOMPLETE FIX
-      H.GetLE(data,16);
-      BN_rshift(H,H,64);
-      BN_lshift(H,H,64);
-      BN_add(H,J,H);
-      BN_rshift(H,H,1<<3);
-      BN_copy(J,H);
-      BN_mask_bits(J,64);
-      cycles=864;
-// END INCOMPLETE FIX
-      break;
     case 0x2F:
-// START INCOMPLETE FIX
-      H.GetLE(data,16);
-      BN_rshift(H,H,64);
-      BN_lshift(H,H,64);
-      BN_add(H,H,J);
-      BN_rshift(J,H,8);
-      BN_mask_bits(J,64);
-      cycles=808;
-// END INCOMPLETE FIX
+      if(l<=0) { cycles+=4; l=wordsize; }
+      else if(l>17) { cycles+=5; l=17; }
+      scalar.GetLE(data,l<<3);
+      AddMapCycles(617+30*wordsize);
+      for(int i=0; i<l; i++) {
+       if(i&1) {
+         unsigned char buf[8];
+         J.PutLE(buf,8);
+         AddMapCycles(10);
+         for(int j=0; j<8; j++) {
+           buf[j]=data[i*8+j];
+           J.GetLE(buf,8);
+           J.SetPos(j+1);
+           AddMapCycles(14);
+           }
+         J.SetPos(0);
+         AddMapCycles(12);
+         }
+       else
+         AddMapCycles(114);
+       }
+      AddMapCycles(3);
+      BN_mul(D,scalar,B,ctx);
+      if(f&1) BN_add(D,D,C);
+      BN_rshift(C,D,l<<6);
+      BN_mask_bits(D,l<<6);
+      D.Commit(l);
+      BN_mask_bits(C,wordsize<<6);
       break;
     case 0x30:
     case 0x31:
       BN_sqr(D,B,ctx);
-      BN_rshift(J,B,((wordsize+1)/2)*128-64);
-      BN_mask_bits(J,64);
       if((f&1)) BN_add(D,D,C);
       BN_rshift(C,D,wordsize<<6);
-      BN_mask_bits(C,wordsize<<6);
-      BN_mask_bits(D,wordsize<<6);
+      BN_rshift(J,B,((wordsize+1)/2)*128-64);
+      BN_mask_bits(J,64);
       break;
     case 0x32:
+      AddMapCycles(1000);
       l=min(34,l);
       if(!BN_is_zero(D)) {
-        A.GetLE(data,l<<3);
-        BN_div(C,B,A,D,ctx);
+        scalar.GetLE(data,l<<3);
+        BN_div(C,B,scalar,D,ctx);
         BN_rshift(A,C,17*64);
-        BN_mask_bits(C,17*64);
         A.Commit(17);
         C.Commit(17);
         }
@@ -307,48 +303,44 @@ bool cMap0101::Map(int f, unsigned char *data, int l)
       AddMapCycles(102);
       MonFin(B,D);
       break;
-    case 0x3a:
-// START INCOMPLETE FIX (this map is normaly in nagra2.c)
-      AddMapCycles(192);
-      IMonInit();
-      MonMul(B,A,B);
-      //MonMul(B,A,B);
-      BN_zero(B);
-// END INCOMPLETE FIX
-      break;
     case 0x3b:
       AddMapCycles(441);
       IMakeJ();
-// START FIX
-      AddMapCycles(327);
-      BN_zero(B);
-      BN_set_bit(B,104);
-      AddMapCycles(46-373);
-// END FIX
+      AddMapCycles(46);
       IMonInit0(wordsize*60+4*l);
-      I.GetLE(data,l<<3);
-      MonMul(B,I,B,l);
+      scalar.GetLE(data,l<<3);
+      MonMul(B,scalar,B,l);
       cycles=tim3b[wordsize-1][l-1]-6;
       break;
     case 0x3d:
-// START INCOMPLETE FIX
-      D.GetLE(data,l<<3);
-      MakeJ0(J,D,C);
-      BN_rshift(A,A,64);
-      BN_mask_bits(A,64);
-      BN_lshift(A,A,64);
-      BN_add(C,A,C);
-      //MonMul0(C,B,B,C,D,J,0);
-// END INCOMPLETE FIX
+      AddMapCycles(652);
+      D.GetLE(data,wordsize<<3);
+      AddMapCycles(514);
+      IMakeJ();
+      AddMapCycles(35);
+      MonMul0(C,B,B,C,D,J,wordsize);
+      AddMapCycles(143);
+      BN_copy(A,B);
+      AddMapCycles(73);
+      MonExpNeg();
+      MonMul(B,A,B);
+      BN_zero(C);
+      cycles+=rand()%(wordsize*20000)+2000;
       break;
     case 0x3c:
     case 0x3e:
+    case 0x46:
       {
-      if(sl==0) cycles+=4;
-      if(l>wordsize) { l=wordsize; cycles+=l>17 ? 9:4; }
-      cBN scalar;
+      if(f==0x46) l=1; else l=sl;
+      if(l<=0) { l=wordsize; cycles+=4; }
+      else if(l>wordsize) { l=wordsize; cycles+=l>17 ? 9:4; }
       scalar.GetLE(data,l<<3);
-      AddMapCycles(441);
+      int sbits=BN_num_bits(scalar);
+      cycles+=3848+((sbits-1)/8)*650 - 11;
+      int msb=data[(sbits-1)/8];
+      for(int i=7; i>=1; --i) if(msb&(1<<i)) { cycles+=i*75-15; break; }
+      for(int i=0; i<sbits; ++i) if(BN_is_bit_set(scalar,i)) cycles+=88;
+      AddMapCycles(f==0x46 ? 400:441);
       if(BN_is_zero(scalar) || BN_num_bits(D)<=1) {
         IMakeJ();
         if(BN_num_bits(D)==1 || !BN_is_zero(scalar)) BN_zero(B);
@@ -360,21 +352,10 @@ bool cMap0101::Map(int f, unsigned char *data, int l)
         MonMul0(B,A,B,C,D,J,0);
         if(f==0x3c) AddMapCycles(2200+(rand()%(wordsize*2000)));
         MonFin(B,D);
-        MonExp(scalar);
         }
       BN_zero(C);
-      int sbits=BN_num_bits(scalar);
-      cycles+=3848+((sbits-1)/8)*650 - 11;
-      int msb=data[(sbits-1)/8];
-      for(int i=7; i>=1; --i) if(msb&(1<<i)) { cycles+=i*75-15; break; }
-      for(int i=0; i<sbits; ++i) if(BN_is_bit_set(scalar,i)) cycles+=88;
-      break;
+      if(f==0x46) cycles-=37;
       }
-    case 0x46:
-// START INCOMPLETE FIX
-      AddMapCycles(328);
-      IMonInit();
-// END INCOMPLETE FIX
       break;
     case 0x4d:
       if(-0x018000==l)
@@ -385,18 +366,12 @@ bool cMap0101::Map(int f, unsigned char *data, int l)
         }
       BN_set_bit(B,0);
       for(int i=0; i<53; i++) data[i]=BN_mod_word(B,primes[i]);
+      BN_copy(A,B);
+      BN_zero(C); BN_zero(D); BN_zero(J);
       break;
     case 0x4e:
-// START INCOMPLETE FIX
-      //MakePrime(B,data);
-      BN_copy(D,B);
-// END INCOMPLETE FIX
-      break;
     case 0x4f:
-// START INCOMPLETE FIX
-      BN_set_word(A,2);
-      BN_add(B,B,A);
-// END INCOMPLETE FIX
+      MakePrime(data,f==0x4f);
       break;
     case 0x57:
 #ifdef HAS_AUXSRV
@@ -406,7 +381,7 @@ bool cMap0101::Map(int f, unsigned char *data, int l)
       }
 #endif
       {
-      cBN a, b, x, y, scalar;
+      cBN a, b, x, y;
       if(l<2 || l>4) l=4;
       WS_START(l);
       l<<=3;
@@ -423,6 +398,7 @@ bool cMap0101::Map(int f, unsigned char *data, int l)
           BN_zero(Px);
           BN_copy(Py,y);
           BN_zero(Qz);
+          MakeJ0(J,D);
           }
         else {
           CurveInit(a);
@@ -450,7 +426,7 @@ bool cMap0101::Map(int f, unsigned char *data, int l)
                 BN_mask_bits(Px,32);
                 BN_lshift(b,Qz,32);
                 BN_add(Px,Px,b);
-                BN_mask_bits(Px,128);
+                BN_mask_bits(Px,l<<3);
                 AddP(0);
                 }
               }
@@ -462,6 +438,7 @@ bool cMap0101::Map(int f, unsigned char *data, int l)
         BN_copy(Px,x);
         BN_copy(Py,y);
         BN_zero(Qz);
+        MakeJ0(J,D);
         }
       memset(data,0,0x40);
       Px.PutLE(&data[0x00],l);
@@ -554,7 +531,7 @@ bool cN2Prov0101::Algo(int algo, const unsigned char *hd, unsigned char *hw)
 
   unsigned char keyNr=(algo>>5)&0x01;
   unsigned char mecmCode[256];
-  GetMem(mecmAddr[keyNr],mecmCode,256,0x80);
+  GetMem(mecmAddr[keyNr],mecmCode,sizeof(mecmCode),0x80);
   cPlainKey *pk;
   unsigned char ideaKey[16];
   if(!(pk=keys.FindKey('N',mecmKeyId,keyNr,sizeof(ideaKey)))) {
@@ -563,11 +540,7 @@ bool cN2Prov0101::Algo(int algo, const unsigned char *hd, unsigned char *hw)
     }
   pk->Get(ideaKey);
   idea.SetEncKey(ideaKey,&ks);
-  for(int i=0x100-8; i>=8; i-=8) {
-    idea.Encrypt(mecmCode+i,8,mecmCode+i,&ks,0);
-    xxor(mecmCode+i,8,mecmCode+i,mecmCode+i-8);
-    }
-  idea.Encrypt(mecmCode,8,mecmCode,&ks,0);
+  idea.Decrypt(mecmCode,sizeof(mecmCode),&ks,0);
   HEXDUMP(L_SYS_RAWECM,mecmCode,sizeof(mecmCode),"decrypted MECM code");
   // check signature
   unsigned char data[256];
@@ -576,8 +549,8 @@ bool cN2Prov0101::Algo(int algo, const unsigned char *hd, unsigned char *hw)
   SHA1(data,sizeof(data)-8,data);
   RotateBytes(data,20);
   if(memcmp(data,mecmCode,8)) {
-     PRINTF(L_SYS_ECM,"%04X: MECM %02x decrypt signature failed",id,keyNr);
-     return false;
+    PRINTF(L_SYS_ECM,"%04X: MECM %02x decrypt signature failed",id,keyNr);
+    return false;
     }
 
   memcpy(hw,hd,seedSize);
@@ -722,7 +695,7 @@ bool cN2Prov0101::ProcessMap(int f)
       DoMap(f);
       break;
     case 0x22:
-      DoMap(f,tmp,(Get(0x48)<<16)|(Get(0x4a)<<8)|Get(0x49));
+      DoMap(f,0,(Get(0x4a)<<8)|Get(0x49));
       break;
     case 0x29:
     case 0x2a:
@@ -752,27 +725,36 @@ bool cN2Prov0101::ProcessMap(int f)
     case 0x38:
     case 0x3a:
     case 0x43:
-    case 0x4f:
       DoMap(f);
       break;
     case 0x44:
     case 0x45:
       GetMem(0x400,tmp,64,0);
+      GetMem(0x440,tmp+64,28,0);
       DoMap(f,tmp,l);
-      SetMem(0x440,tmp,20,0);
-      break; 
+      SetMem(0x400,tmp,64,0);
+      SetMem(0x440,tmp+64,28,0);
+      break;
     case 0x46:
       GetMem(HILO(0x44),tmp,8,0);
-      DoMap(f,tmp,l);
+      DoMap(f,tmp);
       break;
     case 0x4d:
-      DoMap(f,tmp,-((Get(0x48)<<16)|(Get(0x49)<<8)|Get(0x4a)));
-      SetMem(0x400,tmp,53,0);
+      DoMap(f,tmp,-((Get(0x48)<<16)|(Get(0x4a)<<8)|Get(0x49)));
+      if(*tmp==0xff)
+        Set(0x4b,0x00);
+      else {
+        Set(0x4b,*tmp);
+        SetMem(0x400,tmp+1,wordsize*8>53?wordsize*8:53,0x00);
+        }
       break;
     case 0x4e:
-      GetMem(0x400,tmp,53,0);
+    case 0x4f:
+      GetMem(0x400,tmp+1,53,0);
+      if(f==0x4f) GetMem(HILO(0x44),tmp+54,8,0);
       DoMap(f,tmp);
-      SetMem(0x400,tmp,53,0);
+      Set(0x4b,*tmp);
+      SetMem(0x400,tmp+1,53,0);
       break;
     case 0x57:
       addr=HILO(0x46);
@@ -1001,9 +983,9 @@ void cN2Prov0101::TimerHandler(unsigned int num)
 {
   if(hwMapper) {
     int mask=hwMapper->AddCycles(num);
+    if(mask) DisableTimers(13);
     for(int t=0; mask; mask>>=1,t++)
       if(mask&1) {
-        DisableTimers(11);
         if(t==2) {
           PRINTF(L_SYS_EMU,"0101: Timer interrupt %u @ %04x",t,GetPc());
           RaiseException(9);
index 80a01e82a16ca5b6d6873636260794e2d8290d54..ee826ab8ef951a1d48cbb4793d9486ab3aa5f65c 100644 (file)
@@ -97,6 +97,7 @@ bool cN2Prov0501::Algo(int algo, const unsigned char *hd, unsigned char *hw)
     DoMap(EXPORT_C,hw+0x20);
     DoMap(0x43);
     DoMap(0x44,hw);
+    memcpy(hw,hw+64,20);
     hw[0]&=7;
     DoMap(EXPORT_B,hw+3);
     memset(hw+3+0x20,0,128-(3+0x20));
index a510fe8ffe3d26cfa1be38e341bdb099a1eebd51..056490e2488e20e75972e8187c6a431ad79933f4 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <math.h>
 
 #include "system.h"
 #include "opts.h"
 
 cN2Timer::cN2Timer(void)
 {
-  cycles=0; ctrl=0; divisor=1; remainder=-1; latch=0xFF;
+  cycles=intrCycles=delayInterrupt=0; ctrl=0; val=0; divisor=0; invDivisor=0; latch=0xFF;
+  timerBugged=false;
 }
 
 bool cN2Timer::AddCycles(unsigned int count)
 {
-  bool irq=false;
   if(Running()) {
-    bool stop=false;
-    remainder+=count;
-    if(remainder>=divisor) {
-      cycles-=remainder/divisor;
-      remainder%=divisor;
+    cycles+=count;
+    if(!delayInterrupt && InterruptSet() && cycles>=intrCycles) {
+      delayInterrupt=intrCycles-cycles+3;
+      if(delayInterrupt<0) delayInterrupt=count; else delayInterrupt+=count;
+      intrCycles=0;
+      if((ctrl&tmCONTINUOUS)) Update();
       }
-    if(cycles<0 || (cycles==0 && remainder>=2))
-      stop=true;
-    if(ctrl&tmCONTINUOUS) {
-      while(cycles<0) cycles+=latch+1;
+    }
+  if(delayInterrupt) {
+    delayInterrupt-=count;
+    if(delayInterrupt<0) delayInterrupt=0;
+    if(!delayInterrupt) {
+      Update();
+      return true;
       }
-    else if(stop) {
-      PRINTF(L_SYS_EMU,"n2timer %d: timer stop (cycles=%d remainder=%d)",nr,cycles,remainder);
-      cycles=0;
-      Stop();
+    }
+  return false;
+}
+
+double cN2Timer::GetDivisor(void)
+{
+  int prescalar=((ctrl&0x38)>>3)+1;
+  double divisor=pow(4.0,prescalar);
+  double multiplier;
+  // multipliers for 0 and 2 aren't correct yet
+  switch((ctrl&0xc0)>>6) {
+    case 0: multiplier=10.3f;  break;
+    case 1: multiplier= 1.0f;  break;
+    case 2: multiplier=6.375f; break;
+    default:multiplier=0;
+    }
+  return divisor*multiplier;
+}
+
+void cN2Timer::Update(void)
+{
+  int rlatch=latch;
+  if(divisor) {
+    if(timerBugged && cycles>=0x100*divisor) {
+      timerBugged=false;
+      cycles-=0x101*divisor;
       }
-    if((ctrl&tmINTERRUPT) && stop) {
-      PRINTF(L_SYS_EMU,"n2timer %d: IRQ triggered",nr);
-      irq=true;
+    if(timerBugged) rlatch=0x100;
+
+    int v=cycles*invDivisor;
+    if(ctrl&tmCONTINUOUS) {
+      val=rlatch-(v % (rlatch+1));
+      if(InterruptSet() && val==0)
+        intrCycles = cycles + (unsigned int)((val?val:latch)*divisor);
+      }
+    else {
+      // one-shot
+      if(v>=rlatch) {
+        val=0;
+        if(Running() && ((cycles-2)*invDivisor)>=rlatch) Stop();
+        }
+      else
+        val=rlatch-v;
       }
     }
-  return irq;
+  else val=0;
+}
+
+unsigned int cN2Timer::Cycles(void)
+{
+  if(Running()) Update();
+  PRINTF(L_SYS_EMU,"n2timer %d: read %u %02x %02x = %02x",nr,cycles,ctrl&0xff,latch&0xff,val);
+  return val;
+}
+
+unsigned char cN2Timer::Ctrl(void)
+{
+  if(Running()) Update();
+  return ctrl;
 }
 
 void cN2Timer::Latch(unsigned char val)
 {
   if(!Running()) {
     latch=val; if(latch==0) latch=0x100;
-    cycles=latch; remainder=0;
+    cycles=0;
     ctrl|=tmLATCHED;
+    timerBugged=false;
     }
 }
 
@@ -80,19 +134,30 @@ void cN2Timer::Ctrl(unsigned char val)
     ctrl=(ctrl&~tmRUNNING) | (val&tmRUNNING);
     if(!Running()) {
       Stop();
-      PRINTF(L_SYS_EMU,"n2timer %d: stopped cycles=%x ctrl=%x",nr,cycles,ctrl);
+      --cycles;
+      Update();
+      PRINTF(L_SYS_EMU,"n2timer %d: stopped cycles=%d (%02x) ctrl=%02x latch=%02x\n",nr,cycles,val,ctrl,latch&0xff);
       }
     }
   else {
     ctrl=(ctrl&~tmMASK) | (val&tmMASK);
+    divisor=GetDivisor();
+    if(divisor) invDivisor=1.0/divisor;
     if(Running()) {
-      if((ctrl&0xc0)==0x40) divisor=1 << (2 *(1 + ((ctrl & 0x38) >> 3)));
-      else divisor=4; // This is wrong, but we haven't figured the right value out yet
-      if(divisor<=0) divisor=1; // sanity
-      if(!(ctrl&tmLATCHED)) cycles=(unsigned int)(cycles-1)&0xFF;
-      PRINTF(L_SYS_EMU,"n2timer %d: started latch=%x div=%d cycles=%x ctrl=%x",nr,latch,divisor,cycles,ctrl);
-      remainder=-1;
-      if(!(ctrl&tmCONTINUOUS) && cycles==0) ctrl&=~tmRUNNING;
+      if(!(ctrl&tmLATCHED) && divisor>0) {
+        if(this->val==0) {
+          timerBugged=true;
+          cycles=(unsigned int)divisor;
+          }
+        else {
+          cycles=(unsigned int)(((timerBugged?0x100:latch)-this->val+1) * divisor);
+          }
+        }
+      if((ctrl&tmINTERRUPT)) {
+        intrCycles=cycles+(unsigned int)((timerBugged?0x101:this->val?this->val:latch)*divisor);
+        if(nr==2) PRINTF(L_SYS_EMU,"n2timer %d: set for %u cycles (%02x %02x)",nr,intrCycles-cycles,Latch(),ctrl&0xff);
+        }
+      ctrl&=~tmLATCHED;
       }
     }
 }
@@ -100,6 +165,7 @@ void cN2Timer::Ctrl(unsigned char val)
 void cN2Timer::Stop(void)
 {
   ctrl&=~(tmRUNNING|tmLATCHED);
+  if(delayInterrupt>=3) delayInterrupt=0;
 }
 
 // -- cN2CRC -------------------------------------------------------------------
@@ -287,7 +353,8 @@ bool cN2Emu::Init(int id, int romv)
 
 // -- cMapReg ------------------------------------------------------------------
 
-cMapReg::cMapReg(int *_defwordsize, int _maxwordsize)
+cMapReg::cMapReg(int &_pos, int *_defwordsize, int _maxwordsize)
+:pos(_pos)
 {
   SetDefWordSize(_defwordsize);
   SetMaxWordSize(_maxwordsize);
@@ -357,6 +424,7 @@ void cMapReg::GetLE(const unsigned char *in, int n)
   if(wordsize>wsize) Commit();
   reg.GetLE(in,wsize*8);
   Commit(wsize);
+  pos=0;
 }
 
 void cMapReg::PutLE(unsigned char *out, int n)
@@ -364,6 +432,14 @@ void cMapReg::PutLE(unsigned char *out, int n)
   int wsize=OpWordSize(n<=0?n:(n+7)/8);
   Commit();
   fullreg.PutLE(out,wsize*8);
+  if(pos&7) {
+    int s=pos/8*8;
+    for(int i=0; i<(pos&7); i++) {
+      unsigned char c=out[s];
+      memmove(out+s,out+s+1,7);
+      out[s+7]=c;
+      }
+    }
 }
 
 void cMapReg::Set(BIGNUM *val, int wsize)
@@ -392,9 +468,10 @@ void cMapReg::Clear(int wsize)
 // -- cMapMath -----------------------------------------------------------------
 
 cMapMath::cMapMath(void)
-:A(&wordsize),B(&wordsize),C(&wordsize),D(&wordsize),J(0,1),I(&wordsize)
+:A(regpos,&wordsize),B(regpos,&wordsize),C(regpos,&wordsize),D(regpos,&wordsize),J(regpos,0,1),I(regpos,&wordsize)
 {
   wordsize=DEF_WORDSIZE; words=-1;
+  regpos=0;
 }
 
 bool cMapMath::ModAdd(BIGNUM *r, BIGNUM *a, BIGNUM *b, BIGNUM *d)
@@ -539,27 +616,39 @@ void cMapCore::IMakeJ(void)
 
 void cMapCore::IMonInit0(int bits)
 {
-  AddMapCycles(132+(wordsize*8+3)/5*5);
-  if(BN_num_bits(D)>1) AddMapCycles(54);
+  AddMapCycles(114+(wordsize*8+3)/5*5);
+  D.SetPos(2);
+  AddMapCycles(18);
+  if(BN_num_bits(D)>1)
+    for(int i=3; i<=8; i++) {
+      D.SetPos(i);
+      AddMapCycles(9);
+      }
+  D.SetPos(0);
   if(!BN_is_zero(D)) {
     AddMapCycles(54);
     BN_zero(I);
     BN_set_bit(I,bits ? bits : 68*wordsize);
     BN_zero(B);
-    AddMapCycles(141+(wordsize*8+3)/5*5);
-    //BN_set_bit(B,64*(wordsize-1));
-    // TEMP or not?
-    BN_set_bit(B,64*(wordsize-1)+bits);
-    AddMapCycles(92+72*wordsize);
+    AddMapCycles(157+(wordsize*8+3)/5*5+72*(wordsize-1));
+    BN_set_bit(B,64*(wordsize-1));
+    B.SetPos(8*(wordsize-1)+1);
+    AddMapCycles(17);
+    for(int i=8*(wordsize-1)+2; i<=8*wordsize; i++) {
+      B.SetPos(i);
+      AddMapCycles(9);
+      }
+    B.SetPos(0);
+    AddMapCycles(68);
     BN_mod(B,I,D,ctx);
     AddMapCycles(639);
     }
-  AddMapCycles(52);
-  for(int i=0; i<4; i++) {
-    MonMul0(B,B,B,C,D,J,0);
-    AddMapCycles(96+6*(i>0));
-    MonFin(B,D);
-    }
+   AddMapCycles(52);
+   for(int i=0; i<4; i++) {
+     MonMul0(B,B,B,C,D,J,0);
+     AddMapCycles(96+6*(i>0));
+     MonFin(B,D);
+     }
 }
 
 void cMapCore::IMonInit(int bits)
@@ -571,10 +660,12 @@ void cMapCore::IMonInit(int bits)
 void cMapCore::MonInit(int bits)
 {
   // Calculate J0 & H montgomery elements in J and B
-  MakeJ0(J,D);
-  BN_zero(I);
-  BN_set_bit(I,bits ? bits : 68*wordsize);
-  BN_mod(B,I,D,ctx);
+  MakeJ0(J,D,C);
+  if(!BN_is_zero(D)) {
+    BN_zero(I);
+    BN_set_bit(I,bits ? bits : 68*wordsize);
+    BN_mod(B,I,D,ctx);
+    }
   for(int i=0; i<4; i++) MonMul(B,B,B);
 }
 
@@ -592,7 +683,13 @@ void cMapCore::MonExp(BIGNUM *scalar)
 
 void cMapCore::MonExpNeg(void)
 {
-  if(BN_is_zero(D)) { BN_set_word(A,1); return; }
+  if(BN_is_zero(D) || BN_is_word(D,2)) {
+    BN_one(A);
+    if(!BN_is_zero(D)) BN_zero(B);
+    return;
+    }
+  int bits=0;
+  for(int i=BN_num_bits(D)-1; i>=0; i--) if(BN_is_bit_set(D,i)) bits++;
   BN_copy(e,D);
   BN_mask_bits(e,8);           // check LSB
   unsigned int n=BN_get_word(e);
@@ -600,19 +697,15 @@ void cMapCore::MonExpNeg(void)
   if(n) BN_sub_word(e,0x02);   // N -2
   else  BN_add_word(e,0xFE);   // N + 254 ('carryless' -2)
   BN_copy(A,B);
-  for(int i=BN_num_bits(e)-2; i>-1; i--) {
+  for(int i=BN_num_bits(e)-2+(bits==1 && BN_num_bits(D)<=8); i>-1; i--) {
     MonMul(B,B,B);
     if(BN_is_bit_set(e,i)) MonMul(B,A,B);
     }
-  if(BN_is_bit_set(D,0)) {
-    int i;
-    for(i=BN_num_bits(D)-2; i>0; i--) if(BN_is_bit_set(D,i)) break;
-    if(i<=0) {
-      MonMul(B,B,B);
-      MonMul(B,A,B);
-      }
+  if(bits==2 && BN_is_bit_set(D,0)) {
+    MonMul(B,B,B);
+    MonMul(B,A,B);
     }
-  BN_set_word(A,1);
+  BN_one(A);
   MonMul(B,A,B);
 }
 
@@ -747,7 +840,7 @@ void cMapCore::DoMap(int f, unsigned char *data, int l)
   interrupted=false; interruptible=true;
   try {
     if(!Map(f,data,l) && !MapGeneric(f,data,l))
-      PRINTF(L_SYS_MAP,"%04x: unsupported call %02x",mapid,f);
+      PRINTF(L_SYS_MAP,"%04x: unsupported map call %02x",mapid,f);
     if(cycles) {
       unsigned int elapsed=CpuCycles()-startcycles;
       if(cycles>elapsed) AddMapCycles(cycles-elapsed);
@@ -789,7 +882,7 @@ bool cMapCore::MapGeneric(int f, unsigned char *data, int l)
     case IMPORT_LAST:
       if(l>16) { l=1; cycles+=5; }
       else if(l<=0) l=1;
-      cycles=656+160*l-6;
+      cycles+=656+160*l-6;
       regs[last]->GetLE(data,(last==0?1:l)<<3);
       break;
 
@@ -804,14 +897,14 @@ bool cMapCore::MapGeneric(int f, unsigned char *data, int l)
     case EXPORT_D:
       if(l>17) { l=17; cycles+=5; }
       else if(l<=0) { l=wordsize; cycles+=4; }
-      cycles=778+160*l-6;
+      cycles+=778+160*l-6;
       last=f-EXPORT_J;
       regs[last]->PutLE(data,l<<3);
       break;
     case EXPORT_LAST:
       if(l>16) { l=1; cycles+=5; }
       else if(l<=0) l=1;
-      cycles=668+160*l-6;
+      cycles+=668+160*l-6;
       regs[last]->PutLE(data,(last==0?1:l)<<3);
       break;
 
@@ -853,32 +946,68 @@ bool cMapCore::MapGeneric(int f, unsigned char *data, int l)
       AddMapCycles(f==0x39?433:192);
       IMonInit();
       if(f==0x39) {
-        I.GetLE(data,wordsize<<3);
-        MonMul(B,I,B);
+        scalar.GetLE(data,wordsize<<3);
+        MonMul(B,scalar,B);
         }
       else
         MonMul(B,A,B);
+      AddMapCycles(137);
       MonMul(B,A,B);
       break;
     case 0x43: // init SHA1
-      SHA1_Init(&sctx);
-      break;
     case 0x44: // add 64 bytes to SHA1 buffer
-      RotateBytes(data,64);
-      SHA1_Update(&sctx,data,64);
-      BYTE4_LE(data   ,sctx.h4);
-      BYTE4_LE(data+4 ,sctx.h3);
-      BYTE4_LE(data+8 ,sctx.h2);
-      BYTE4_LE(data+12,sctx.h1);
-      BYTE4_LE(data+16,sctx.h0);
+      if(f==0x43) {
+        l=0;
+        SHA1_Init(&sctx);
+        cycles=885;
+        }
+      else {
+        l=64;
+        RotateBytes(data,l);
+        sctx.h4=UINT32_LE(data+l+0x00);
+        sctx.h3=UINT32_LE(data+l+0x04);
+        sctx.h2=UINT32_LE(data+l+0x08);
+        sctx.h1=UINT32_LE(data+l+0x0c);
+        sctx.h0=UINT32_LE(data+l+0x10);
+        sctx.Nl=UINT32_LE(data+l+0x14);
+        sctx.Nh=UINT32_LE(data+l+0x18);
+        sctx.num=0;
+        SHA1_Update(&sctx,data,l);
+        memset(data,0,l);
+        cycles=65309;
+        }
+      if(data) {
+        BYTE4_LE(data+l+0x00,sctx.h4);
+        BYTE4_LE(data+l+0x04,sctx.h3);
+        BYTE4_LE(data+l+0x08,sctx.h2);
+        BYTE4_LE(data+l+0x0c,sctx.h1);
+        BYTE4_LE(data+l+0x10,sctx.h0);
+        BYTE4_LE(data+l+0x14,sctx.Nl);
+        BYTE4_LE(data+l+0x18,sctx.Nh);
+        }
       break;
-    case 0x45: // add wordsize bytes to SHA1 buffer and finalize SHA result
-      if(dl) {
-        if(dl>1) RotateBytes(data,dl);
-        SHA1_Update(&sctx,data,dl);
+    case 0x45: // add l bytes to SHA1 buffer and finalize SHA result
+      if(l<=0 || l>64)
+        cycles=465+5*(l>64);
+      else {
+        sctx.h4=UINT32_LE(data+0x40);
+        sctx.h3=UINT32_LE(data+0x44);
+        sctx.h2=UINT32_LE(data+0x48);
+        sctx.h1=UINT32_LE(data+0x4c);
+        sctx.h0=UINT32_LE(data+0x50);
+        sctx.Nl=UINT32_LE(data+0x54);
+        sctx.Nh=UINT32_LE(data+0x58);
+        sctx.Nl&=~0xff;
+        sctx.num=0;
+        if(l>1) RotateBytes(data,l);
+        SHA1_Update(&sctx,data,l);
+        SHA1_Final(data+0x40,&sctx);
+        RotateBytes(data+0x40,20);
+        BYTE4_LE(data+0x54,sctx.Nl);
+        BYTE4_LE(data+0x58,sctx.Nh);
+        memset(data,0,64);
+        cycles=65519+(64-l)*12+(l>=56)*64681-16*(l==64);
         }
-      SHA1_Final(data,&sctx);
-      RotateBytes(data,20);
       break;
     default:
       return false;
@@ -1323,7 +1452,7 @@ void cSystemNagra2::ProcessEMM(int pid, int caid, unsigned char *buffer)
   if(emmP) emmP->PostDecrypt(false);
 
   HEXDUMP(L_SYS_RAWEMM,emmdata,cmdLen,"Nagra2 RAWEMM");
-  id=(emmdata[8]<<8)+emmdata[9];
+  if(buffer[0]==0x82) id=(emmdata[8]<<8)+emmdata[9];
   LBSTARTF(L_SYS_EMM);
   bool contFail=false;
   for(int i=8+2+4+4; i<cmdLen-22; ) {
@@ -1399,12 +1528,19 @@ void cSystemNagra2::ProcessEMM(int pid, int caid, unsigned char *buffer)
           }
         break;
         }
+      case 0xA0: i+=5; break;                   // CamID target update
+      case 0xA3: i+=4; break;                   // Cam group target update
       case 0xA4: i+=emmdata[i+1]+2+4; break;   // conditional (always no match assumed for now)
-      case 0xA6: i+=15; break;
+      case 0xA6:                                // updates tier/blackout
+        if(emmdata[i+4]==0xFD && emmdata[i+5]==0x98) i+=(emmdata[i+1]+emmdata[i+3]+4);
+        else i+=15;
+        break;
       case 0xAA: i+=emmdata[i+1]+5; break;
       case 0xAD: i+=emmdata[i+1]+2; break;
       case 0xA2:
-      case 0xAE: i+=11;break;
+      case 0xAE: i+=11; break;
+      case 0x01: i+=3; break;
+      case 0x10: i+=18; break;
       case 0x12: i+=emmdata[i+1]+2; break;      // create tier
       case 0x20: i+=19; break;                  // modify tier
       case 0x9F: i+=6; break;
@@ -1425,6 +1561,10 @@ void cSystemNagra2::ProcessEMM(int pid, int caid, unsigned char *buffer)
           }
         break;
         }
+      case 0x13:
+      case 0x14:
+      case 0x15:
+      case 0x85:
       case 0xE1:
       case 0xE2:
       case 0x00: i=cmdLen; break;              // end of processing
index 24f7a958fae0ca69d535f9fe459dc89929c48e3c..c959e184159a51385bfbe340b17b5a32c279b962 100644 (file)
@@ -56,7 +56,7 @@ extern char auxPassword[250];
 class cMapReg {
 private:
   cBN fullreg, reg, tmp;
-  int wordsize, maxwordsize, *defwordsize;
+  int wordsize, maxwordsize, *defwordsize, &pos;
   //
   int DefWordSize() const { return defwordsize ? *defwordsize:DEF_WORDSIZE; }
   int OpWordSize(int wsize) const { int sz=wsize>0 ? wsize:DefWordSize(); return sz>maxwordsize ? maxwordsize:sz; }
@@ -68,7 +68,7 @@ protected:
   void SetMaxWordSize(int max) { maxwordsize=max; }
   void SetDefWordSize(int *_defwordsize) { defwordsize=_defwordsize; }
 public:
-  cMapReg(int *_defwordsize=0, int _maxwordsize=DEF_MAXWORDSIZE);
+  cMapReg(int &_pos, int *_defwordsize=0, int _maxwordsize=DEF_MAXWORDSIZE);
   operator BIGNUM* () { return Value(); }
   BIGNUM *operator->() { return Value(); }
   BIGNUM *Value(int wsize=0, bool mask=false);
@@ -78,6 +78,7 @@ public:
   void PutLE(unsigned char *out, int n=0);
   void Set(BIGNUM *val, int wsize);
   void Clear(int wsize);
+  void SetPos(int _pos) { pos=_pos; }
   };
 
 // ----------------------------------------------------------------
@@ -90,8 +91,9 @@ private:
   cBN x, y, s;
   int words;
 protected:
-  int wordsize;
-  cMapReg A, B, C, D, J, I, H;
+  int wordsize, regpos;
+  cMapReg A, B, C, D, J, I;
+  cBN H, scalar;
   cBNctx ctx;
   SHA_CTX sctx;
   // stateless
@@ -186,16 +188,23 @@ public:
 
 class cN2Timer {
 private:
-  int ctrl, divisor, cycles, remainder, latch, nr;
+  int ctrl, latch, nr, delayInterrupt;
+  unsigned char val;
+  unsigned int cycles, intrCycles;
+  double divisor, invDivisor;
+  bool timerBugged;
   enum { tmCONTINUOUS=0x01, tmRUNNING=0x02, tmINTERRUPT=0x04, tmMASK=0xFF, tmLATCHED=0x100 };
   //
   bool Running(void) { return ctrl&tmRUNNING; }
+  bool InterruptSet(void) { return ctrl&tmINTERRUPT; }
   void Stop(void);
+  double GetDivisor(void);
+  void Update(void);
 public:
   cN2Timer(void);
   bool AddCycles(unsigned int count);
-  unsigned int Cycles(void) { return cycles; }
-  unsigned char Ctrl(void) { return ctrl&tmMASK; }
+  unsigned int Cycles(void);
+  unsigned char Ctrl(void);
   void Ctrl(unsigned char c);
   unsigned char Latch(void) { return latch&0xFF; }
   void Latch(unsigned char val);