Subversion Repositories HomeAutomation

Rev

Rev 101 | Blame | Last modification | View Log | SVN | RSS feed

  1. /*********************************************************************
  2.  *
  3.  *                  Main application
  4.  *
  5.  *********************************************************************
  6.  * FileName:        $HeadURL: https://svn.auml.se/svn/HomeAutomation/trunk/EmbeddedSoftware/PIC/canBoot/Source/Main.c $
  7.  * Last changed:    $LastChangedDate: 2006-11-15 10:22:35 +0100 (Wed, 15 Nov 2006) $
  8.  * By:              $LastChangedBy: johboh $
  9.  * Revision:        $Revision: 104 $
  10.  ********************************************************************/
  11.  
  12. #include <compiler.h>
  13. #include <stackTasks.h>
  14. #include <CANdefs.h>
  15. #include <CAN.h>
  16. #include <boot.h>
  17. #include <funcdefs.h>
  18. #include <EEaccess.h>
  19.  
  20. /** V E C T O R  R E M A P P I N G *******************************************/
  21.  
  22. #pragma code _HIGH_INTERRUPT_VECTOR = 0x000008
  23. void _high_ISR (void)
  24. {
  25.     _asm goto RM_HIGH_INTERRUPT_VECTOR _endasm
  26. }
  27.  
  28. #pragma code _LOW_INTERRUPT_VECTOR = 0x000018
  29. void _low_ISR (void)
  30. {
  31.     _asm goto RM_LOW_INTERRUPT_VECTOR _endasm
  32. }
  33.  
  34. #pragma code
  35.  
  36.  
  37. #ifdef USE_CAN
  38.     #include <CAN.h>
  39. #endif
  40.  
  41. static CAN_PROTO_MESSAGE cm;
  42. static DWORD cnt =0;
  43.  
  44. static void mainInit(void);
  45. static BOOL canGetPacket(void);
  46. void TBLWT_Func(void);
  47. void TBLWTPOSTINC_Func (void);
  48. void TBLRD_Func(void);
  49.  
  50. #define TICK_SUB_COUNTER_10MS 312
  51. #define PACKET_TIMEOUT 14
  52. #define BOOTLOADER_INIT_TIMOUT 500
  53.  
  54. DWORD tickCounter = 0;
  55. DWORD tickSubCounter = 0;
  56.  
  57. DWORD tickGet(void);
  58.  
  59. static int MY_ID = DEFAULT_ID;
  60. static BYTE MY_NID = DEFAULT_NID;
  61.  
  62. void main()
  63. {
  64.     static PROGRAM_STATE pgs = pgsWATING_START;
  65.     static DWORD t = 0;
  66.     static WORD programmerSid=0;
  67.    
  68.     static BYTE errMsg = ACK_ERR_NO_ERROR;
  69.     static PROGRAM_STATE afterAckPgs = pgsWATING_START;
  70.     static DWORD pgmAddress = 0;
  71.     static BYTE bytesReceived = 0;
  72.  
  73.     DWORD tBoot;
  74.    
  75.     static BYTE pgmData[64];
  76.  
  77.     // Inits
  78.     mainInit();
  79.  
  80.     #ifdef USE_CAN
  81.         canInit();
  82.     #endif
  83.  
  84.     tBoot = tickGet();
  85.    
  86.     // Read ID and NID if exist.
  87.     if (EERead(NODE_ID_EE)==NODE_HAS_ID)
  88.     {
  89.         MY_ID=(((WORD)EERead(NODE_ID_EE + 1))<<8)+EERead(NODE_ID_EE + 2);
  90.         MY_NID=EERead(NODE_ID_EE + 3);
  91.     }
  92.  
  93.  
  94.     for(;;)
  95.     {
  96.         BYTE i;
  97.         CAN_PROTO_MESSAGE outCm;
  98.         BOOL hasPacket = canGetPacket();
  99.  
  100.         tickSubCounter++;
  101.         if (tickSubCounter>TICK_SUB_COUNTER_10MS)
  102.         {
  103.             tickCounter++;
  104.             tickSubCounter=0;
  105.             ClrWdt();
  106.         }
  107.  
  108.         switch(pgs)
  109.         {
  110.             case pgsWATING_START:
  111.                 // Check if there is bootloader start packet
  112.                 if (hasPacket==TRUE && cm.funct==FUNCT_BOOTLOADER && cm.funcc==FUNCC_BOOT_INIT && ((((unsigned int)cm.data[BOOT_DATA_RID_HIGH_INDEX])<<8)+cm.data[BOOT_DATA_RID_LOW_INDEX])==MY_ID && cm.nid==MY_NID)
  113.                 {
  114.                     // Has new start packet
  115.                     pgmAddress      = (((DWORD)cm.data[BOOT_DATA_ADDRU_INDEX])<<16)+(((DWORD)cm.data[BOOT_DATA_ADDRH_INDEX])<<8)+((DWORD)cm.data[BOOT_DATA_ADDRL_INDEX]);
  116.                     programmerSid   = cm.sid;
  117.                     t               = tickGet();
  118.                     errMsg          = ACK_ERR_NO_ERROR;
  119.                     afterAckPgs     = pgsWAIT_FIRST_PGM_PACKET;
  120.                     pgs             = pgsSEND_ACK;
  121.                     // save pgm adress.
  122.                    
  123.                 }
  124.  
  125.                 // Bootloader timeout
  126.                 if ((tickGet()-tBoot)>BOOTLOADER_INIT_TIMOUT)
  127.                 {
  128.                     pgs = pgsDONE;
  129.                 }
  130.             break;         
  131.  
  132.             case pgsSEND_ACK:
  133.                 // send ack
  134.                 outCm.funct             = FUNCT_BOOTLOADER;
  135.                 outCm.funcc             = FUNCC_BOOT_ACK;
  136.                 outCm.nid               = MY_NID;
  137.                 outCm.sid               = MY_ID;
  138.                 outCm.data_length       = 8;
  139.                 outCm.data[BOOT_DATA_ERR_INDEX] = errMsg;
  140.                 t                       = tickGet();
  141.                 pgs                     = afterAckPgs;
  142.  
  143.                 while(!canSendMessage(outCm,PRIO_HIGH));
  144.             break;
  145.  
  146.  
  147.             case pgsWAIT_FIRST_PGM_PACKET:
  148.                 // wait for first pgm packet.
  149.                 if ((tickGet()-t)>PACKET_TIMEOUT)
  150.                 {
  151.                     // Timeout, resend ack.
  152.                     errMsg          = ACK_ERR_NO_ERROR;
  153.                     afterAckPgs     = pgsWAIT_FIRST_PGM_PACKET;
  154.                     pgs             = pgsSEND_ACK;
  155.                 }
  156.  
  157.                 // If programming packet...
  158.                 if (hasPacket==TRUE && cm.funct==FUNCT_BOOTLOADER && (cm.funcc & 0x3E3)==FUNCC_BOOT_PGM && cm.nid==MY_NID && cm.sid==programmerSid)
  159.                 {
  160.                     BYTE localIndex = (BYTE)((cm.funcc & 0x1C)>>2)*8;
  161.  
  162.                     // Save bytes in memory, setup start adress etc..
  163.                     bytesReceived   = 8;       
  164.                     pgmData[localIndex+0]=cm.data[0]; pgmData[localIndex+1]=cm.data[1]; pgmData[localIndex+2]=cm.data[2]; pgmData[localIndex+3]=cm.data[3];
  165.                     pgmData[localIndex+4]=cm.data[4]; pgmData[localIndex+5]=cm.data[5]; pgmData[localIndex+6]=cm.data[6]; pgmData[localIndex+7]=cm.data[7];
  166.                
  167.                     pgs             = pgsWAIT_PGM_PACKET;
  168.                 }
  169.  
  170.  
  171.             break;
  172.  
  173.  
  174.             case pgsWAIT_PGM_PACKET:
  175.        
  176.            
  177.                 // If programming packet...
  178.                 if (hasPacket==TRUE && cm.funct==FUNCT_BOOTLOADER && (cm.funcc & 0x3E3)==FUNCC_BOOT_PGM && cm.nid==MY_NID && cm.sid==programmerSid)
  179.                 {
  180.                     BYTE localIndex = (BYTE)((cm.funcc & 0x1C)>>2)*8;
  181.                     // Save bytes in memory, inc received bytes.
  182.                     pgmData[localIndex+0]=cm.data[0]; pgmData[localIndex+1]=cm.data[1]; pgmData[localIndex+2]=cm.data[2]; pgmData[localIndex+3]=cm.data[3];
  183.                     pgmData[localIndex+4]=cm.data[4]; pgmData[localIndex+5]=cm.data[5]; pgmData[localIndex+6]=cm.data[6]; pgmData[localIndex+7]=cm.data[7];
  184.                    
  185.                     bytesReceived   += 8;
  186.                     pgs              = pgsWAIT_PGM_PACKET;
  187.                 }
  188.  
  189.                 // If adress packet (control packet)..
  190.                 if (hasPacket==TRUE && cm.funct==FUNCT_BOOTLOADER && cm.funcc==FUNCC_BOOT_ADDR && cm.nid==MY_NID && cm.sid==programmerSid)
  191.                 {
  192.                     // Send ack and goto wait first packet.
  193.                     // save adress.
  194.                     errMsg          = ACK_ERR_NO_ERROR;
  195.                     afterAckPgs     = pgsWAIT_FIRST_PGM_PACKET;
  196.                     pgs             = pgsSEND_ACK;
  197.                 }
  198.                
  199.                 // If end packet...
  200.                 if (hasPacket==TRUE && cm.funct==FUNCT_BOOTLOADER && cm.funcc==FUNCC_BOOT_DONE && cm.nid==MY_NID && cm.sid==programmerSid)
  201.                 {
  202.                     // write program.
  203.                     // If no bytes received at all, assume end.
  204.                     // If not 64 bytes received, send error ack and wait for first packet.
  205.                     // If 64, write.
  206.  
  207.                     if (bytesReceived==0)
  208.                     {
  209.                         // No received, done.
  210.                         errMsg          = ACK_ERR_NO_ERROR;
  211.                         afterAckPgs     = pgsDONE;
  212.                         pgs             = pgsSEND_ACK;
  213.                     }
  214.                     else if (bytesReceived!=64)
  215.                     {
  216.                         errMsg          = ACK_ERR_ERROR;
  217.                         afterAckPgs     = pgsWAIT_FIRST_PGM_PACKET;
  218.                         pgs             = pgsSEND_ACK;
  219.                     }
  220.                     else
  221.                     {
  222.                         pgs              = pgsWRITE_PROGRAM;
  223.                         afterAckPgs     = pgsDONE;
  224.                     }
  225.                 }
  226.  
  227.                 // If bytes received 64, write..
  228.                 if (bytesReceived==64)
  229.                 {
  230.                     LED0_IO=~LED0_IO;
  231.                     pgs              = pgsWRITE_PROGRAM;
  232.                     afterAckPgs     = pgsWAIT_PGM_PACKET;
  233.                     // TODO! Send ack more than once..
  234.                 }
  235.  
  236.  
  237.             break;
  238.  
  239.  
  240.             case pgsWRITE_PROGRAM:
  241.  
  242.                 ClrWdt();
  243.  
  244.                 // Write program and send ack.
  245.  
  246.                 // Check addr limit
  247.                 if (pgmAddress<RM_RESET_VECTOR)
  248.                 {              
  249.                     errMsg          = ACK_ERR_ERROR;
  250.                     pgs             = pgsSEND_ACK;
  251.                     //Not allowed to write here!
  252.                    
  253.                 }
  254.  
  255.                 //Load Table pointer registers with base address to erase.
  256.                 TBLPTRU = (BYTE)((pgmAddress & 0xFF0000)>>16);
  257.                 TBLPTRH = (BYTE)((pgmAddress & 0xFF00)>>8);
  258.                 TBLPTRL = (BYTE)(pgmAddress & 0xFF);
  259.  
  260.                 // point to Flash program memory
  261.                 EECON1bits.EEPGD = 1;
  262.                 // access Flash program memory
  263.                 EECON1bits.CFGS = 0;
  264.                 // enable write to memory
  265.                 EECON1bits.WREN = 1;
  266.                 // enable Row Erase operation
  267.                 EECON1bits.FREE = 1;
  268.                 // disable interrupts
  269.                 INTCONbits.GIE = 0;
  270.  
  271.                 // Do it!
  272.                 EECON2 = 0x55;
  273.                 EECON2 = 0xAA;
  274.                 EECON1bits.WR = 1;
  275.  
  276.                 // enable interrupts
  277.                 INTCONbits.GIE = 1;
  278.                
  279.                 //Load Table pointer registers with base address to program.
  280.                 TBLPTRU = (BYTE)((pgmAddress & 0xFF0000)>>16);
  281.                 TBLPTRH = (BYTE)((pgmAddress & 0xFF00)>>8);
  282.                 TBLPTRL = (BYTE)(pgmAddress & 0xFF);
  283.  
  284.                 // Load bytes
  285.                 for(i=0;i<63;i++)
  286.                 {
  287.                     TABLAT=pgmData[i];
  288.                     TBLWTPOSTINC_Func();
  289.                 }
  290.                 TABLAT=pgmData[63];
  291.                 TBLWT_Func();
  292.  
  293.  
  294.                 // point to Flash program memory
  295.                 EECON1bits.EEPGD = 1;
  296.                 // access Flash program memory
  297.                 EECON1bits.CFGS = 0;
  298.                 // enable write to memory
  299.                 EECON1bits.WREN = 1;
  300.                 // disable interrupts
  301.                 INTCONbits.GIE = 0;
  302.                
  303.                 // Do it!
  304.                 EECON2 = 0x55;
  305.                 EECON2 = 0xAA;
  306.                 EECON1bits.WR = 1;
  307.  
  308.                 // enable interrupts
  309.                 INTCONbits.GIE = 1;
  310.  
  311.                 // disable write to memory
  312.                 EECON1bits.WREN = 0;
  313.  
  314.                 pgmAddress     += 64;
  315.                 bytesReceived   = 0;
  316.                 errMsg          = ACK_ERR_NO_ERROR;
  317.                 pgs             = pgsSEND_ACK;
  318.  
  319.             break;
  320.  
  321.  
  322.             case pgsDONE:
  323.                 _asm  goto RM_RESET_VECTOR _endasm
  324.             break;
  325.  
  326.        
  327.  
  328.  
  329.             default:
  330.                 pgs = pgsWATING_START;
  331.             break;
  332.         }
  333.    
  334.  
  335.     }
  336. }
  337.  
  338.  
  339. /*
  340. *   Function: mainInit
  341. *
  342. *   Input:  none
  343. *   Output: none
  344. *   Pre-conditions: none
  345. *   Affects: Se code
  346. *   Depends: none.
  347. */
  348. void mainInit()
  349. {
  350.     LED0_TRIS=0;   
  351.  
  352.     // Enable internal PORTB pull-ups
  353.     INTCON2bits.RBPU = 0;
  354.    
  355.     // Enable Interrupts
  356.     INTCONbits.GIEH = 1;
  357.     INTCONbits.GIEL = 1;
  358.  
  359.     // Enable interrupt prirority
  360.     RCONbits.IPEN=1;
  361.    
  362.  
  363. }
  364.  
  365.  
  366.  
  367. /// CAN-------------------------------------------------------
  368.  
  369.  
  370. #ifdef USE_CAN
  371.  
  372.  
  373. /*
  374. *   Function: canInit
  375. *
  376. *   Input:  none
  377. *   Output: none
  378. *   Pre-conditions: none
  379. *   Affects: Changes can registers.
  380. *   Depends: none.
  381. */
  382. void canInit()
  383. {
  384.     CANCON = 0x80; //request config mode
  385.     while ((CANSTAT & 0xE0) != 0x80 ); //wait for config mode request
  386.  
  387.     // BAUD AND TQS
  388.  
  389.     BRGCON1=0;
  390.    
  391.     // Sync_Seq = 2 x TQ = 1
  392.     BRGCON1bits.SJW1=0;
  393.     BRGCON1bits.SJW0=1;
  394.    
  395.     // Setting BRP.
  396.     BRGCON1=BRGCON1|CAN_BRP;
  397.    
  398.     // Maximum PHEG1
  399.     BRGCON2bits.SEG2PHTS = 1;
  400.    
  401.     // Phase_Seg1 = 2 x TQ = 1
  402.     BRGCON2bits.SEG1PH2 = 0;
  403.     BRGCON2bits.SEG1PH1 = 0;
  404.     BRGCON2bits.SEG1PH0 = 1;
  405.    
  406.     // Prop_Seq = 2 x TQ = 1
  407.     BRGCON2bits.PRSEG2 = 0;
  408.     BRGCON2bits.PRSEG1 = 0;
  409.     BRGCON2bits.PRSEG0 = 1;
  410.    
  411.     //  Enableing bus wake-up
  412.     BRGCON3bits.WAKDIS = 0;
  413.    
  414.     // Dont use filter when wakeup
  415.     BRGCON3bits.WAKFIL = 0;
  416.    
  417.     // Phase_Seg2 = 2 x TQ = 1
  418.     BRGCON3bits.SEG2PH2 = 0;
  419.     BRGCON3bits.SEG2PH1 = 0;
  420.     BRGCON3bits.SEG2PH0 = 1;
  421.  
  422.     ECANCON=0;
  423.     ECANCONbits.MDSEL1 = 1;
  424.     ECANCONbits.MDSEL0 = 0;
  425.  
  426.  
  427.     // FILTERS AND MASKS
  428.     RXB0CONbits.RXM1=1;
  429.     RXB0CONbits.RXM0=0;
  430.  
  431.  
  432.     // INTERRUPTS
  433.    
  434.     // Diable transmit interrupt
  435.     TXBIE = 0;
  436.  
  437.     //rx no interrupt
  438.     PIR3 = 0;
  439.     PIE3 = 0;
  440.  
  441.  
  442.     // Programmable no buffers interrupt
  443.     BIE0  = 0x00;  
  444.  
  445.  
  446.     //loopback mode or not
  447.     #ifdef CAN_LOOPBACK_MODE
  448.         CANCON = 0x40;//request loopback mode
  449.         while ((CANSTAT & 0xE0) != 0x40 );//wait for loopback mode
  450.     #else
  451.         CANCON = 0x00;//request normal mode
  452.         while ((CANSTAT & 0xE0) != 0x00 );//wait for normal mode
  453.     #endif
  454.  
  455. }
  456.  
  457.  
  458.  
  459.  
  460. /*
  461. *   Function: canGetPacket
  462. *
  463. *   Input:  none
  464. *   Output: none
  465. *   Pre-conditions: a call to canISR and an packet is available and banked to RXB0
  466. *   Affects: Calls canParse() function in main for parsing received packet.
  467. *   Depends: none.
  468. */
  469. BOOL canGetPacket()
  470. {
  471.  
  472.         // Get which buffer that is ful.
  473.         ECANCON=(ECANCON&0b00000)|(0b10000|(CANCON&0x0F));
  474.         if (!RXB0CONbits.RXFUL) return FALSE;
  475.    
  476.         //RXB0SIDH 28 27 26 25 24 23 22 21
  477.         //RXB0SIDL 20 19 18 xx xx xx 17 16
  478.         //RXB0EIDH 15 14 13 12 11 10 09 08
  479.         //RXB0EIDL 07 06 05 04 03 02 01 00
  480.  
  481.         //funct xx xx xx xx 28 27 26 25
  482.         //funcc xx xx xx xx xx xx 24 23 22 21 20 19 18 17 16 15
  483.         //nid   xx xx 14 13 12 11 10 09
  484.         //sid   xx xx xx xx xx xx xx 08 07 06 05 04 03 02 01 00
  485.  
  486.  
  487.         cm.funct=((RXB0SIDH & 0xF0)>>4);
  488.         cm.funcc=(((WORD)(RXB0SIDH & 0x0F))<<6)+(((WORD)(RXB0SIDL & 0xE0))>>2)+(((WORD)(RXB0SIDL & 0x03))<<1)+(((WORD)(RXB0EIDH & 0x80))>>7);
  489.         cm.nid=((RXB0EIDH & 0x7E)>>1);
  490.         cm.sid=(((WORD)(RXB0EIDH & 0x01))<<8)+RXB0EIDL;
  491.  
  492.  
  493.         // Data length
  494.         cm.data_length=(RXB0DLCbits.DLC3<<3)+(RXB0DLCbits.DLC2<<2)+(RXB0DLCbits.DLC1<<1)+RXB0DLCbits.DLC0;
  495.  
  496.         // Data one bye one, for speed
  497.         cm.data[0]=RXB0D0;  cm.data[1]=RXB0D1;
  498.         cm.data[2]=RXB0D2;  cm.data[3]=RXB0D3;
  499.         cm.data[4]=RXB0D4;  cm.data[5]=RXB0D5;
  500.         cm.data[6]=RXB0D6;  cm.data[7]=RXB0D7;
  501.  
  502.         // Clear ful status
  503.         RXB0CONbits.RXFUL=0;
  504.  
  505.         return TRUE;
  506. }
  507.  
  508.  
  509. /*
  510. *   Function: canSendMessage
  511. *
  512. *   Input:  CAN_MESSAGE to send
  513. *   Output: True if packet scheduled sucess, false otherwise.
  514. *   Pre-conditions: canInit
  515. *   Affects: ..
  516. *   Depends: ..
  517. */
  518. BOOL canSendMessage(CAN_PROTO_MESSAGE cm,CAN_PRIORITY prio)
  519. {
  520.    
  521.     if ( TXB0CONbits.TXREQ == 0 )  { ECANCON=(ECANCON&0b00000)|0b00011; }
  522.     if ( TXB1CONbits.TXREQ == 0 )  { ECANCON=(ECANCON&0b00000)|0b00100; }
  523.     if ( TXB2CONbits.TXREQ == 0 )  { ECANCON=(ECANCON&0b00000)|0b00101; }
  524.     // None of the transmit buffers were empty.
  525.     else { return FALSE;}
  526.  
  527.     if (prio<0 || prio>3) prio = 0;
  528.  
  529.  
  530.         //RXB0SIDH 28 27 26 25 24 23 22 21
  531.         //RXB0SIDL 20 19 18 xx xx xx 17 16
  532.         //RXB0EIDH 15 14 13 12 11 10 09 08
  533.         //RXB0EIDL 07 06 05 04 03 02 01 00
  534.  
  535.         //funct xx xx xx xx 28 27 26 25
  536.         //funcc xx xx xx xx xx xx 24 23 22 21 20 19 18 17 16 15
  537.         //nid   xx xx 14 13 12 11 10 09
  538.         //sid   xx xx xx xx xx xx xx 08 07 06 05 04 03 02 01 00
  539.  
  540.         RXB0SIDH = (BYTE)((cm.funct & 0x0F)<<4)+(BYTE)((cm.funcc & 0x03C0)>>6);
  541.         RXB0SIDL = (BYTE)((cm.funcc & 0x003F)<<2)+(BYTE)((cm.funcc & 0x0006)>>1);
  542.         RXB0EIDH = (BYTE)((cm.funcc & 0x0001)<<7)+(BYTE)((cm.nid & 0x3F)<<1)+(BYTE)((cm.sid & 0x0100)>>8);
  543.         RXB0EIDL = (BYTE)((cm.sid & 0x00FF));
  544.  
  545.         // Data length
  546.         RXB0DLC = 0;
  547.         if (cm.data_length>8) RXB0DLC=8; else RXB0DLC=cm.data_length;
  548.        
  549.         // NOT Remote request
  550.         _asm
  551.             bcf RXB0DLC, 6, 0
  552.         _endasm
  553.  
  554.  
  555.         // Data one bye one, for speed
  556.         RXB0D0=cm.data[0];  RXB0D1=cm.data[1];
  557.         RXB0D2=cm.data[2];  RXB0D3=cm.data[3];
  558.         RXB0D4=cm.data[4];  RXB0D5=cm.data[5];
  559.         RXB0D6=cm.data[6];  RXB0D7=cm.data[7];
  560.  
  561.         // set extended or not
  562.         RXB0SIDLbits.EXID=1;
  563.  
  564.         // Priority
  565.         RXB0CON = (RXB0CON & 0b11111100) | prio;
  566.  
  567.         // mark as redy to transmit
  568.         _asm
  569.             bsf RXB0CON, 3, 0
  570.         _endasm
  571.  
  572.         return TRUE;
  573.  
  574. }
  575.  
  576.  
  577.  
  578. #endif //USE_CAN
  579.  
  580.  
  581.  
  582.  
  583. void TBLRD_Func()
  584. {
  585. _asm
  586.     TBLRD
  587. _endasm
  588. }
  589.  
  590. void TBLWT_Func()
  591. {
  592. _asm
  593.     TBLWT
  594. _endasm
  595. }
  596.  
  597.  
  598. void TBLWTPOSTINC_Func (void){
  599. _asm
  600.     TBLWTPOSTINC
  601. _endasm
  602. }
  603.  
  604. DWORD tickGet()
  605. {
  606.     return tickCounter;
  607. }
  608.  
  609.  
  610. #pragma code user = RM_RESET_VECTOR
  611.