In 16-bit Borland-land, I use to modify the real-time clock using outp(
0x40... and 0x43), and redirect interrupt 1C. Ironically, I could only do
fixed-point stuff in there, so I would just use it as a pre-emptive counter
and had a forground task that would act like non-preemptive distributor.
Does anyone have a good example of implementing a high-speed interrupt like
this? I've searched high and low and have had little success.
As a fall-back, does anyone have an example of a micro-second timer. Clock()
only has a 1ms resolution, if I had something that had a 25us resolution I
could do exactly what I am doing with Borland.
BTW, I am also using Wat-32 for ethernet access. Is this also going to give
me problems?
Any help would be greatly appreciated.
-Doug
In general, the more detailed definition of "no luck" you provide, the
more specific answer you'll get.
> In 16-bit Borland-land, I use to modify the real-time clock using outp(
> 0x40... and 0x43), and redirect interrupt 1C. Ironically, I could only do
> fixed-point stuff in there, so I would just use it as a pre-emptive counter
> and had a forground task that would act like non-preemptive distributor.
>
16-bit DOS is no different from 32-bit DOS in the way the hardware
functions, but care has to be taken when writing and installing
interrupt handlers etc.
> Does anyone have a good example of implementing a high-speed interrupt like
> this? I've searched high and low and have had little success.
>
The RTC might be an option. But if the PIT doesn't work for you, RTC
probably won't either.
Michal
Thanks for replying:
For starters I am using 32-bit protected model using the DOS4GW extender
and I am running in FreeDOS.
Let me define no luck:
I was able to setup the interrupt in this environment, but it wouldn't go
any faster than 18.2 hertz,.So I guess it would be more accurate to say I am
unsuccesful in accomplishing my desired result.
> 16-bit DOS is no different from 32-bit DOS in the way the hardware
> functions, but care has to be taken when writing and installing interrupt
> handlers etc.
'Care' can you be a little more specific?
Do you have a coding snippit of setting up the RTC? That would be helpful.
Technically the PIT did work, I just couldn't change the clock time... in
addition it would be really nice for my interrupt to be completely
preemptive. But i need floating point in the forground and background tasks.
Again, thanks for replying.
-Doug
"Michal Necasek" <mnec...@yahoo.com> wrote in message
news:fgf9ng$muf$1...@www.openwatcom.org...
I have some possibly (but not necessarily)
useful code. What's your email address?
Steve.
Any help would be greatly appreciated. Here is my email:
DJo...@FirstFlite.net
Thanks in advance,
Doug
"Steve Adam" <Steve....@remove.gmail.com> wrote in message
news:fgfbs6$o4h$1...@www.openwatcom.org...
> For starters I am using 32-bit protected model using the DOS4GW extender
> and I am running in FreeDOS.
>
Okay, then there should be no fundamental differences from existing
16-bit DOS code. The reason I was asking is that if you were trying to
run the code under eg. Windows NT, the hardware manipulation would be
highly unlikely to work.
> Let me define no luck:
> I was able to setup the interrupt in this environment, but it wouldn't go
> any faster than 18.2 hertz,.So I guess it would be more accurate to say I am
> unsuccesful in accomplishing my desired result.
>
So you installed your own interrupt handler and the handler was
getting called, but it was only getting called at the default ~18.2Hz rate?
How exactly are you programing the PIT? From your description it
sounds like the port writes aren't getting through at all, only I can't
imagine how that might happen.
>> 16-bit DOS is no different from 32-bit DOS in the way the hardware
>> functions, but care has to be taken when writing and installing interrupt
>> handlers etc.
>
> 'Care' can you be a little more specific?
>
It's good to use the runtime library or OS provided interrupt vector
get/set services as direct manipulation of the interrupt table will
definitely not work (unless you really know what you're doing and
properly update the real-mode table and the protected-mode IDT).
But that doesn't seem to be the issue here.
> Do you have a coding snippit of setting up the RTC? That would be helpful.
>
Not really, but you shouldn't *have* to use the RTC instead of the PIT...
> Technically the PIT did work, I just couldn't change the clock time... in
> addition it would be really nice for my interrupt to be completely
> preemptive. But i need floating point in the forground and background tasks.
>
FPU access from interrupt handlers is a little bit tricky :) You
might be able to save the FPU state, do your bit, and restore the
original state (FNSAVE/FRSTOR), but an interrupt handler should really
only do what it absolutely has to and return.
Michal
> So you installed your own interrupt handler and the handler was getting
> called, but it was only getting called at the default ~18.2Hz rate?
>
That is correct.
> How exactly are you programing the PIT? From your description it sounds
> like the port writes aren't getting through at all, only I can't imagine
> how that might happen.
Here is the snippet that set up the Timing:
/*
** This routine set time of day to a given frequency in Hertz
** and returns that exact delta time with respect to the integer
** trunacation. The following flags can be set:
**
** _flg bit 0 = 1 To perform time set;
** _flg bit 0 = 0 To perform reset to original value;
** _flg bit 1 = 1 Display data!
**
** Also returns approximate number of new frames in a normal
** 18.2 Hz Interupt
*/
float SET_DAY_TIMER( float _freq, int _flg, int *_nof )
{
float _t, _o1, _o2;
int _i, _lsb, _msb;
if ( _flg & 1 )
{
_t = 1193180.0 / _freq;
_i = _t;
_o1 = 1193180.0 / ( (float) _i );
_o2 = 1.0 / _o1;
_lsb = _i & 0x00FF;
_msb = ( _i & 0xFF00 ) >> 8;
_nof[0] = (int) ( 65535.0 / ( (float) _i ) );
outp( 0x43, 0x36 );
outp( 0x40, _lsb );
outp( 0x40, _msb );
}
else
{
outp( 0x43, 0x36 );
outp( 0x40, 0 );
outp( 0x40, 0 );
_lsb = 0;
_msb = 0;
_o1 = 1193180.0 / 65535.0;
_o2 = 1.0 / _o1;
_nof[0] = 1;
};
if ( _flg & 2 )
{
printf( "freq = %8.2f Delta Time = %8.6f LSB = 0x%02X",
_o1,
_o2,
_lsb );
printf( " MSB = 0x%02X Frames: %4d\r\n",
_msb,
_nof[0] );
};
return( _o2 );
};
>
> FPU access from interrupt handlers is a little bit tricky :) You might
> be able to save the FPU state, do your bit, and restore the original state
> (FNSAVE/FRSTOR), but an interrupt handler should really only do what it
> absolutely has to and return.
>
Would that be implemented like this?:
#pragma aux FP_SAVE = "FNSAVE";
#pragma aux FP_RESTORE = "FRSTOR";
...
void interrupt MyISR
{
FP_SAVE();
...
FP_RESTOR()
};
Thanks for the help
-Doug
but 25us resolution is 40.000 interrupts/sec (=40 kHz). This might be too
much, especially if you are using int 1Ch.
> BTW, I am also using Wat-32 for ethernet access. Is this also going to give
> me problems?
Yes. See TIMER.C in WATT32.
If you don't need an interrupt, but simply to measure time with that
resolution, consider using a combination of the 18.2 ms software counter and
the hardware contents of the timer register itself.
Wilton
>> So you installed your own interrupt handler and the handler was getting
>> called, but it was only getting called at the default ~18.2Hz rate?
>>
> That is correct.
>
Very weird.
> outp( 0x43, 0x36 );
> outp( 0x40, _lsb );
> outp( 0x40, _msb );
>
This looks right, with the caveat that you should be disabling
interrupts (pushf/cli/.../popf) for the timer programming.
Again, a DOS extender does not affect how hardware functions. If you
run plain DOS, especially without EMM386, then 32-bit or 16-bit DOS
isn't all that different. That's why I don't understand how
reprogramming the timer might have no effect.
> _lsb = 0;
> _msb = 0;
> _o1 = 1193180.0 / 65535.0;
>
Shouldn't that be 65536? Count value of 65535 would be 0xFFFF, count
value of 0 is 65536...
Further note is that you shouldn't be reprogramming timer 0 and
hooking interrupt 1Ch. You should be hooking interrupt 08h and calling
1Ch at the standard 18.2Hz rate, otherwise anything dependent on stable
timing (like DOS clock or perhaps a network stack) is going to get confused.
As others said, high interrupt rates may be a problem. When using DOS
extenders, it may be helpful to write bi-modal interrupt handlers so
that real/protected mode switching is avoided. However, this depends on
the DOS extender used. If you have a fast CPU, IMO you should be able to
handle 5kHz interrupt rate. Today's operating systems may run the system
timer at ~1kHz (1024Hz), they wouldn't be doing that if processing the
interrupts was eating significantly into the total CPU time available.
Michal
If I'm reading the PIT documentation (helppc)
correctly, 0x36 sets counter 0 to mode 3,
(square wave generator). I'm not sure how
that influences the timing of the interrupt
but mode 2 (0x34) might be a better choice.
Steve.
> If I'm reading the PIT documentation (helppc)
> correctly, 0x36 sets counter 0 to mode 3,
> (square wave generator).
>
Yes.
> I'm not sure how
> that influences the timing of the interrupt
> but mode 2 (0x34) might be a better choice.
>
Not really. Mode 3 is the mode normally used for timer 0.
Michal
Thanks, I didn't know that.
Your statement caused me to quickly review
some of my old code. I found I was using
mode 2 in some code which reads the value
of the counter from a PIT (on an I/O card,
not the system timer). Mode 3 causes the
timer to decrement by 2 so I was getting
wrong results until I changed to mode 2.
Steve.
1) I didn't have _disable()/_enable() around the time. Good call!
2) Someone suggested that Watt-32 might be getting in my way, but I got
confirmation that it is not using Intr 0x08.
3) I have a 1Ghz processor, so I am not that worried about the ISR overhead.
4) I didn't have outp( 0x20, 0x20 ) at the end of my interrupt.
Wilton Helm had the best suggestion though. I don't really need the
interrupt. Just use the timer0 registers and keep track of the 18.2 hz
interrupts. I think I am going to do this only use the clock() command to
veryify that it was less than 55ms (18.2Hz) since the last update. This is
elegant on a lot of levels.
-Doug
"Michal Necasek" <mnec...@yahoo.com> wrote in message
news:fgn1cl$71u$1...@www.openwatcom.org...
Thanks for you input:
> If you don't need an interrupt, but simply to measure time with that
> resolution, consider using a combination of the 18.2 ms software counter
> and the hardware contents of the timer register itself.
>
This was the best idea yet. If I get not benifit of an interrupt, why have
it? I all need is a good counter. Thanks for the great idea! I wrote a
routine that would track Timer0, and use the clock() to give me a ball park
for the modulo it was one.
-Doug
>> Not really. Mode 3 is the mode normally used for timer 0.
Steve is correct on this: I'm pretty sure mode 2 is the correct one to use.
-Doug
"Steve Adam" <Steve....@remove.gmail.com> wrote in message
news:fgn4i4$971$1...@www.openwatcom.org...
>
> 1) I didn't have _disable()/_enable() around the time. Good call!
>
Probably not the cause of the problem though...
> 2) Someone suggested that Watt-32 might be getting in my way, but I got
> confirmation that it is not using Intr 0x08.
>
Actually, *you* might be getting in the way of Watt-32 if it relies on
the 18.2Hz timer and you change that.
> 3) I have a 1Ghz processor, so I am not that worried about the ISR overhead.
>
Right, 1Ghz should handle that.
> 4) I didn't have outp( 0x20, 0x20 ) at the end of my interrupt.
>
Definitely a problem :) If you don't do an EOI, the PIC thinks your
routine is still in service (corresponding ISR bit is still set) and you
won't get another interrupt. I spent quite a bit of time figuring out
this sort of bug once...
> Wilton Helm had the best suggestion though. I don't really need the
> interrupt. Just use the timer0 registers and keep track of the 18.2 hz
> interrupts. I think I am going to do this only use the clock() command to
> veryify that it was less than 55ms (18.2Hz) since the last update. This is
> elegant on a lot of levels.
>
If you don't need an interrupt, consider using timer 2 instead of timer 0.
Michal
Wilton
Doug Joseph 02.11.07 16:33 wrote:
> Hi I am trying to make a 5Khz interrupt in DOS4GW mode and I am having no
> luck.
> In 16-bit Borland-land, I use to modify the real-time clock using outp(
> 0x40... and 0x43), and redirect interrupt 1C.
Very, very bad. If you reprogram system timer, you should also trap
INT8 (INT1C called from INT8).
> Ironically, I could only do
> fixed-point stuff in there, so I would just use it as a pre-emptive counter
> and had a forground task that would act like non-preemptive distributor.
> Does anyone have a good example of implementing a high-speed interrupt like
> this? I've searched high and low and have had little success.
Find and read PCTIM003.TXT:
"
Description: FAQ / Application notes: Timing on the PC family under DOS
Author: Kris Heidenstrom (khei...@actrix.gen.nz)
Version: 19951220, Release 3
_ The DOS and BIOS date/time and alarm functions
_ The BIOS tick count variable
_ Trapping and handling critical errors
_ Using interrupt 1C hex and interrupt 8
_ The counter/timer's internal operation
_ Reprogramming the timer operating mode
_ Measuring short time intervals (three techniques)
_ Reading the timer count in progress
_ Generating an absolute timestamp
_ Reprogramming the timer tick rate
_ Simulating a vertical retrace interrupt for triple buffering
_ Using the serial and parallel port interrupts
_ Reading the joystick position (three methods)
_ Generating tones and sound.
"
This is very comprehensive source about timing on PC, including very high
precise and very accurate timing.
> As a fall-back, does anyone have an example of a micro-second timer. Clock()
> only has a 1ms resolution,
It does NOT have 1ms resolution. Ie., it returns value in msec, but its
step under DOS is ~55 msec (from BIOS timer counter resolution).
Doug Joseph 02.11.07 17:15 wrote:
> preemptive. But i need floating point in the forground and background tasks.
FP in handler at speed of 5kHz... This may waste too much of CPU time
(for save/restore FP state and to do heavy calculations). You should try to
avoid as much, as possible ANY (hard) working in interrupt handlers, this is
dangerous (for system stability and responsibility) and cause lost of
interrupts. Redefine your concept of "background", if plain queue of
"actions todo" isn't fit your purposes.
Doug Joseph 02.11.07 18:45 wrote:
> float _t, _o1, _o2;
> _t = 1193180.0 / _freq;
> _o1 = 1193180.0 / ( (float) _i );
> _o2 = 1.0 / _o1;
> _nof[0] = (int) ( 65535.0 / ( (float) _i ) );
Wow. So much of FP calculation where only int calculations is enough.
Michal Necasek 05.11.07 15:48 wrote:
>> I'm not sure how
>> that influences the timing of the interrupt
>> but mode 2 (0x34) might be a better choice.
> Not really. Mode 3 is the mode normally used for timer 0.
"
## 7.4.2 CTC CHANNEL ZERO DEFAULT OPERATING MODE
Traditionally, CTC channel zero has been set to operate in mode three by the
BIOS POST, but recent 486 BIOSes that I have seen appear to be using mode two
by default. The only significant differences are the width of the pulse from
the CTC pin that triggers the timer tick interrupt, which is narrow in mode
two but is still plenty wide enough for the Intel 8259 PIC chip, and the value
read from the CTC channel zero counter (which decrements twice as quickly in
mode 3).
From a hardware point of view, either mode should work on all motherboards, but
if some code in the BIOS assumes that CTC channel 0 is in the mode that the BIOS
originally programmed, it may not work correctly if CTC channel 0 has been
reprogrammed for the other. Of course, reprogramming the CTC divisor for a
higher sample rate will also cause this problem. The only example of this that
I know of, is the joystick read function (int 15h called with AH = 84h and
DX = 1) (see section пп 10.4.2). Please tell me if you find any other problems
related to changing the mode. (*)
> Find and read PCTIM003.TXT:
http://www.heidenstrom.gen.nz/kris.html#mystuff
--
Robert Riebisch
Bitte NUR in der Newsgroup antworten!
Please reply to the Newsgroup ONLY!
Here is what I ended up doing:
It ain't pretty, but it is works pretty well. I tested over 55ms and it
works well (most of the code is to handle this case), but it is perfectly
accurate under 55ms (to about 1usec).
I know you, for some reason, don't like floating point, but it is a
beautiful thing in my world.
-Doug
=---
File: XClock.h
#include <time.h>
clock_t ms_Clock_Tick = 0;
DWORD us_Clock_Tick = 0;
#define T0TICKSPERSEC 1193180.0
#define T0FREQ 1193180.0 / 65536.0
#define XCLOCK_MINDT 1.0 / T0FREQ
void Init_ClockX( void );
float Get_ClockX( DWORD flags );
...
void Init_ClockX( void )
{
BYTE clsb, cmsb;
ms_Clock_Tick = clock();
clsb = inp( 0x40 );
cmsb = inp( 0x40 );
us_Clock_Tick = clsb + ( cmsb << 8 );
};
//
// flags - 0x01 reset counter
//
float Get_ClockX( DWORD flags )
{
BYTE clsb, cmsb;
DWORD DelClock;
clock_t new_ms_Clock_Tick;
DWORD new_us_Clock_Tick;
float dclock;
float cxdt;
int xn;
new_ms_Clock_Tick = clock();
// at the 24th hour, the clock-ticks are restart. (We'll blow this
frame)
if ( ms_Clock_Tick > new_ms_Clock_Tick )
{
if ( ms_Clock_Tick != new_ms_Clock_Tick )
{
ms_Clock_Tick = 0;
};
};
dclock = (float)( new_ms_Clock_Tick - ms_Clock_Tick ) / CLOCKS_PER_SEC;
clsb = inp( 0x40 );
cmsb = inp( 0x40 );
new_us_Clock_Tick = clsb + ( cmsb << 8 );
if ( us_Clock_Tick < new_us_Clock_Tick )
{
DelClock = 65536 - us_Clock_Tick - new_us_Clock_Tick;
}
else
{
DelClock = us_Clock_Tick - new_us_Clock_Tick;
};
cxdt = DelClock / T0TICKSPERSEC;
if ( dclock > XCLOCK_MINDT )
{
xn = (int)( dclock / XCLOCK_MINDT );
cxdt += (float)( xn * XCLOCK_MINDT );
};
if ( flags & 0x01 )
{
us_Clock_Tick = new_us_Clock_Tick;
ms_Clock_Tick = new_ms_Clock_Tick;
};
return( cxdt );
};
-===
All of my work is on CPUs that don't have an FPU so it is about 100 times
slower.
It also takes more memory to store data.
Also, while the dynamic range is high, round off is always lurking.
Sometimes that's OK, sometimes its not.
It has its place (I would hate to do a least squares curve fit or other
matrix inversion type operation in integer), but on the hardware I use, I
have to think about three or four times before deciding to use it.
Beyond that, particularly when dealing with hardware devices (which almost
always use integer) or other domains where the data nicely fits in integer
form (well behaved dynamic range) I try to stay with integers. It takes a
little thought to handle scaling properly, but it is often worth it.
I got used to handling the scaling when CPUs rarely had FPUs and memory was
precious. On a current generation Pentium class processor, I suspect there
is little difference.
Glad you have a solution.
Wilton
As an old 6502 programmer (Pre-apple II), I know what you mean about
integers math. A FP unit is a nice luxury of this modern era of DSP's and
x86. Round off error is every bit as much an issue with fixed point as it is
with floating point. On a Pentium 4 i beleive a floating point operation
takes 6 to 8 cycles. If you are doing trig functions this is wonderful. If
you are doing mults and adds it is not quite as good as fixed point math.
It is funny, because I've been working with a bunch of Arm7's lately and
have had to re-learn my many fixed-point tricks to make things efficient. It
has really taken me back a bit, but it has been kinda fun!
I guess it is all in the application.
Thanks for the input and happy coding.
-Doug
> .... Round off error is every bit as much an issue with fixed point as it
> is with floating point. ...
I don't know your application, but if you care about precision, integer
arith is also more precise than float IF you have some knowledge on
the dynamic range of your signals.
To avoid the problem of having to re-invent an whole math library, many
DSP vendors have code for math functions free for download.
Mark
"Doug Joseph" <DJo...@FirstFlite.net> wrote in message
news:fhamoa$a53$1...@www.openwatcom.org...
> On a Pentium 4 i beleive a floating point operation
>takes 6 to 8 cycles. If you are doing trig functions this is wonderful. If
>you are doing mults and adds it is not quite as good as fixed point math.
Those guesses at cycle timing are way off, best consult Agner Fog's
brilliant documentation.
http://www.agner.org/optimize/#manuals
Fixed point maths is fun though :)
JimS
While agner may be correct on many things, Let me quote Intel:
"3 GHz a Pentium 4 can do around 600 million floating point additions per
second. If your code is well designed and if there aren't too many
dependencies, these processors can do a floating point add every cycle - so
that 3 GHz Pentium 4 can actually do about 3 billion additions per second!
That's pretty amazing considering that these calculations are being done
with up to 19 decimal digits of accuracy."
This is circa 2003, I am guessing that the Athlon 64, and the latest
Pentiums processors are even more efficient than this.
-Doug
"JimS" <so...@not.com> wrote in message
news:0hrij3hdr55idkt7l...@4ax.com...
You are so right that fixed point has its place, but using the Pentium FPU,
math is done using an 80-bit format of which 59-bits are the mantissa. I am
betting you aren't using 59 bits in the fixed-point world.
Fixed point is a wonderful thing on smaller machines, but floating point is
mighty efficient on these modern monsters.
-Doug
"Mark Wezelenburg" <mark_wez...@edpnet.be> wrote in message
news:fhbm7p$517$1...@www.openwatcom.org...
>Round off error is every bit as much an issue with fixed point as it is
>with floating point.
That depends. No sane person would ever write a financial application using
floating point because of the audit requirement for everything to come out
to the exact penny. Also, given the same number of bits, total, an integer
representation can always represent a larger range without loosing any
significance. Here is a somewhat contrived example of what I was referring
to. Assume that x is a 32 bit float with 24 bit mantissa and 8 bit
exponent:
for (x = 8E6; x < 200E6; x += 10.0)
printf("%f", x);
First, there will be round off errors, because the numbers are represented
in fractional form rather than integer and therefore cannot exactly
represent certain integer quantities. I picked a range where the LSB is
very involved. As x gets larger, it even drops of LSBs so it may go by
steps of 8 or 12 instead of 10. Finally, unbeknown to the casual observer,
this is an infinite loop. Before it gets to 200 million, the granularity is
larger than 10, so adding 10 will produce no change! A similar problem
occurs when taking the difference between two numbers of this magnitude.
Use those 32 bits for an integer, and none of these problems will show up in
the range specified, or for quite a while afterward. What is acceptable
depends a lot on the problem at hand.
Wilton
How I wish it were true! My brokerage converts the plain decimal report I
can cut & paste from the text report (into a parsable text file) to decimal
printout of double precision FP - in dollars - for Intuit's .QIF format. I
gave up using Quicken... BTW, complaint to the IT department were ignored.
--
Steve