Timer routines

Posted on 2002-03-22
Last Modified: 2006-11-17
I need to measure the time in microseconds between state changes on the serial port's DSR pin.

I am reading the state of the DSR pin by executing as fast as possible the following code:

#define COM1_UART       0x3f8         // Port address of COM1
#define COM2_UART       0x2f8         // Port address of COM2

#define MSR                 6         // Modem Status register offset
#define CTS_DELTA        0x01         // Clear To Send Changed
#define DSR_DELTA        0x02         // Data Set Ready Changed
#define RI_DELTA         0x04         // Ring Indicator Changed
#define CD_DELTA         0x08         // Carrier Detect Changed
#define CTS              0x10         // Clear To Send
#define DSR              0x20         // Data Set Ready
#define RI               0x40         // Ring Indicator
#define CD               0x80         // Carrier Detect

// get start time here
while ( ((msr=inportb(COM1_UART+MSR))&DSR_DELTA)==0 )
// get end time here

Anyone care to write an assembler routine that I can easily invoke from 'C' that returns the elapsed time in microseconds - from the time the routine is invoked until the state of the DSR pin changes. Call this routine "waitDSR"

Also need a very similar routine that sets the state of the DSR pin and then waits for the specified number of microseconds. call this routine "setDSR".

With the following conditions:

I want to include the assembly code in my C program using the asm {...} inline compiler directive.

The time delay must be specified/returned in microseconds as 32 bit value.
Since the embedded C compiler I am using does not support 32 bit long values it needs to be set/returned as a C data type declared as: char usTime[4];

With 32 bits the largest measurable delay is about 4294 seconds. The largest time delay I will need to measure is about 5 seconds. So ignore any wrapping/overflow of the 32 bit value. The smallest time delay I need to measure is 40 microseconds.

I would prefer the Pentium processor time stamp be used to calculate the time delay.

I do not know x86 assembly so lots of comments would be much appreciated.


P.S. The application for this code is to encode/decode a variable pulse width electronic signal, using the DSR pin to sense the waveform. The current application source code, which does not use the embedded C compiler just normal VC++, can be downloaded from if anyone is interested.

If anyone has any suggestions to improve the accuracy of the timing I'd be happy to hear them.
Question by:zebada
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 6
  • 4
LVL 49

Expert Comment

ID: 6897104
What if DSR never changes?  Do you want a timeout to occur?  

Can you be certain that the processor is Pentium?  Are ou running under Windows OS or some embedded OS I've never heard of (if Windows, the port I/O will likely fail unless this is part of a device driver... also under a preemprive multi-tasking O/S, a tight wait loop might be interrupted and mess up the timing).

-- Dan

Author Comment

ID: 6900519
Hi Dan,

Oops, yes I forgot to mention a timeout value, in milliseconds, or cpu cycles, is necessary.

The software is a currently only for prototyping, I have a pentium so it's not an issue - yet.

It will be running under an embedded operating system that boots from floppy - so no DOS, no Windows, no Nothing - just the hadrware, the pentium and my code (in .com file format).

What I am hoping to acheive is to figure out the following:

I will be receiving a square wave pulse that may be as short as 40us. I need to time the response of the DRS as seen by the software. If it is close enough to the 40us (+/-10us) then I will go ahead and try to develop the software to use the DSR pin.
If it is not close enough I will not be able to use the DSR pin - I may need to look at using the parallel port pins instead.

Basically what I am trying to find is a communications pin on the PC that I can use that will respond in real-time within 10us of the actual state change on that pin.

It may be that the DSR line signal takes too long to filter through the UART and into the "port" io-byte. Maybe it is only a few microseconds - I need to know, that's all.


LVL 49

Expert Comment

ID: 6901210
>>I would prefer the Pentium processor time stamp be used to calculate the time delay.

That could be a problem: The Pentiun Timestamp obtained via RDTSC does not directly yield a value in milliseconds -- it just gets an arbitrary tick-count.  It's Windows that provides the conversion to let you equate the counter to an actual time value.  

Will you be running under a standard PC BIOS?  There is a BIOS fn that will yield a duration in milliseconds.  It may not be accurate enough.  By default (at least, the last time I checked), it is too inaccurate since it only updates in 55-ms increments.  However, given the two, it should be possible to equate a timer tick to a number of RDTSC ticks and thus get a good time duration.

Have you thought about using the LPT port?  I have heard of people using, e.g., the paper-out pin for simple external device communications.

-- Dan
Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.


Author Comment

ID: 6901308
The conversion from CPU clock cycles to microseconds is simple - divide the CPU clock cycles by the Mhz of the CPU.
Or is it not that simple?

You're right, the motherboard ctc (clock timer/counter) chip is what I am currently using its about 1.1Mhz so I can measure sub-usec timings but I think it is not accurate/stable for what I am trying to do.

I don't want to go chasing other pins until I *know* that the DSR pin will *not* do what I want.
LVL 49

Expert Comment

ID: 6909436
My solution is a blend of asm and C.  There is no performance penalty, and it is easier to understand.  

Note that I could not test this code because Windows does not let me do an IN from port (this is a protected opcode, allowed only by ring-0 device drivers).

Furthermore, you will need to determine CNUM_TicksPerSec for the target computer.

#define COM1_UART       0x3f8         // Port address of COM1
#define COM2_UART       0x2f8         // Port address of COM2

#define MSR                 6         // Modem Status register offset
#define DSR              0x20         // Data Set Ready

#define CNUM_TicksPerSec 0x10000

// WaitForDSR( COM1_UART+MSR, 3 );

// returns ticks until DSR becomes set
// ticks are arbitrary units, and differ depending upon CPU and speed
// you will need to set CNUM_TicksPerSec for your computer
LONGLONG qwCntStart= 0;
LONGLONG qwCntCur=   0;
LONGLONG qwCntMax=   0;
BOOL fDone= 0;

int WaitForDSR( int nPort, int nMaxSecs )
      //----------------------- get starting tick
      __asm {
            mov ecx,0
            mov DWORD PTR qwCntStart, eax
            mov DWORD PTR qwCntStart+4, edx
      qwCntMax= qwCntStart + (LONGLONG)CNUM_TicksPerSec * nMaxSecs;
      qwCntCur= qwCntStart;

      fDone= 0;
      while ( !fDone && (qwCntCur < qwCntMax) ) {
            __asm {
            mov DWORD PTR qwCntCur, eax
            mov DWORD PTR qwCntCur+4, edx
            mov dx, WORD PTR nPort

            in al,dx  // <<<<<< err when using this (protected OS)
            // mov al, 0x20 // <<<<<<<<<<< i used this for testing
            and al, BYTE PTR DSR
            jnz keepGoing
            mov fDone, 1
      int nRetVal= (int)(qwCntCur - qwCntStart);
      if ( !fDone ) nRetVal= -1;

      return( nRetVal );

//In case your compiler does not support LONGLONG (aka, _int64)
//here is an example of how to subtract one from another in ASM
//mov  edx,dword ptr [qwCntStart+4]
//sub  edx,dword ptr [qwCntCur+4]
//mov  eax,dword ptr [qwCntStart]
//sbb  eax,dword ptr [qwCntCur]
//mov  dword ptr [nRetVal],edx

Infact, here is the ASM for the whole fn:

mov         [qwCntStart (00416878)],eax
mov         dword ptr [qwCntStart+4 (0041687c)],edx

142:      qwCntMax= qwCntStart + (LONGLONG)CNUM_TicksPerSec * nMaxSecs;
mov         eax,dword ptr [ebp+0Ch]
push        0
push        1000h
push        edx
push        eax
call        _allmul (004018b0)
mov         ecx,dword ptr [qwCntStart (00416878)]
add         ecx,eax
mov         eax,[qwCntStart+4 (0041687c)]
adc         eax,edx
mov         dword ptr [qwCntMax (00416888)],ecx
mov         [qwCntMax+4 (0041688c)],eax
143:      qwCntCur= qwCntStart;
mov         ecx,dword ptr [qwCntStart (00416878)]
mov         dword ptr [qwCntCur (00416880)],ecx
mov         edx,dword ptr [qwCntStart+4 (0041687c)]
mov         dword ptr [qwCntCur+4 (00416884)],edx
145:      fDone= 0;
mov         dword ptr [fDone (00416890)],0
146:      while ( !fDone && (qwCntCur < qwCntMax) ) {
cmp         dword ptr [fDone (00416890)],0
jne         keepGoing+2 (00402206)
mov         eax,[qwCntCur+4 (00416884)]
cmp         eax,dword ptr [qwCntMax+4 (0041688c)]
jg          keepGoing+2 (00402206)
jl          WaitForDSR+9Eh (004021de)
mov         ecx,dword ptr [qwCntCur (00416880)]
cmp         ecx,dword ptr [qwCntMax (00416888)]
jae         keepGoing+2 (00402206)

mov         ecx,0
mov         [qwCntCur (00416880)],eax
mov         dword ptr [qwCntCur+4 (00416884)],edx
mov         dx,word ptr [ebp+8]
154:          //in al,dx
mov         al,20h
and         al,20h
jne         keepGoing (00402204)
mov         dword ptr [fDone (00416890)],1
159:          keepGoing:
160:          }
161:      }
jmp         WaitForDSR+78h (004021b8)
162:      int nRetVal= (int)(qwCntCur - qwCntStart);
mov         edx,dword ptr [qwCntCur (00416880)]
sub         edx,dword ptr [qwCntStart (00416878)]
mov         eax,[qwCntCur+4 (00416884)]
sbb         eax,dword ptr [qwCntStart+4 (0041687c)]
mov         dword ptr [ebp-4],edx
163:      if ( !fDone ) nRetVal= -1;
cmp         dword ptr [fDone (00416890)],0
jne         keepGoing+2Ch (00402230)
mov         dword ptr [ebp-4],0FFFFFFFFh
165:      return( nRetVal );
mov         eax,dword ptr [ebp-4]
166:  }

-- Dan
LVL 49

Expert Comment

ID: 6909439
Ignore that part about "here is how to subtract one QWORD from another"  Correct code is as shown in the last part of the post:

mov   edx,dword ptr [qwCntCur]
sub   edx,dword ptr [qwCntStart]
mov   eax,[qwCntCur+4]
sbb   eax,dword ptr [qwCntStart+4]
mov   dword ptr [nRetVal],edx

-- Dan
LVL 49

Expert Comment

ID: 6917637
hi zebada,
Do you have any additional questions?  Do any comments need clarification?

-- Dan

Author Comment

ID: 6917715
No, I just haven't had time to look at it yet. Hopefully make some time in the next few weeks.
I have to set up all the hardware stuff and wire it all up before I can start testing.
Thanks for the code I'll try to get it working/conpiled and tested - I'll probably be asking q's about it later.
LVL 49

Accepted Solution

DanRollins earned 100 total points
ID: 7101437
hi zebada,
Do you have any additional questions?  Do any comments need clarification?

-- Dan

Author Comment

ID: 7101519
I still haven't found time to test out the code but when I do get time I will give it a go. It's a good starting point anyway.

Featured Post

Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article will help to fix the below error for MS Exchange server 2010 I. Out Of office not working II. Certificate error "name on the security certificate is invalid or does not match the name of the site" III. Make Internal URLs and External…
Popular third-party chat platforms like Slack, Discord, and Telegram are just a few of the many new productivity applications that are being hijacked by cybercriminals to create command-and-control (C&C) communications infrastructures for their malw…
This video Micro Tutorial shows how to password-protect PDF files with free software. Many software products can do this, such as Adobe Acrobat (but not Adobe Reader), Nuance PaperPort, and Nuance Power PDF, but they are not free products. This vide…
There are cases when e.g. an IT administrator wants to have full access and view into selected mailboxes on Exchange server, directly from his own email account in Outlook or Outlook Web Access. This proves useful when for example administrator want…

729 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question