Subversion Repositories HomeAutomation

Rev

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

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