Announcement

Collapse
No announcement yet.

Exact timing

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Exact timing

    I was always looking for a way to control the timing of a program more
    exactly than it is possible with TIMER (e.g. for animations, music, etc).
    MTIMER did not provide any solution because you can call this function only
    once and must then reset it (which includes, as I found, waiting for the
    next "regular" timer tick). Finally I decided to write my own Interrupt 8
    handler in order to control the frequency of the timer tick (see code below).
    It works quite fine so far, but I still have some questions concerning
    running these routines under Windows 95/98:
    - In my experience it was not possible to set the timer to a higher frequency
    than about 1000 Hz. When setting it to any value between 1000 and 2000 Hz,
    the real frequency remained at 1000 Hz. Above 2000 Hz, a message box appears:
    "This program probably does not run well under Windows." (CPU speed cannot
    be the reason; the speed of my system is 266 MHz),
    - I tried the same code WITHOUT calling the original INT 8 handler. It would
    be expected that this would freeze the system time at the point when the new
    interrupt handler was installed. Under DOS, it actually does, but under
    Windows it did not affect the system time at all (although it affected the
    value of TIMER in PowerBASIC).
    - In conclusion: Could anybody explain me what exactly Windows is doing with
    the timer, and if this could result in unexpected problems using the code
    posted below? ...and maybe somebody has a hint how to increase the clock
    frequency to more than 1000 Hz (under Windows)?

    Hans Ruegg.


    Code for Timer routines:
    ========================
    DEFINT A-Z

    DECLARE SUB setpithandler (count??, pithandler??())
    DECLARE FUNCTION getpit() AS DWORD
    DECLARE FUNCTION pitcheck() AS WORD
    DECLARE SUB pitfreq (freq)
    DECLARE SUB resetpithandler ()

    DIM tmr??(28) 'array which holds code for the new INT 8 handler
    CLS
    setpithandler (&H1234DD\1000), tmr??() 'set clock frequency to 1000Hz
    PRINT "New handler installed."
    DO:
    LOCATE 3,1: PRINT USING "##########"; getpit;
    LOCATE 3,40: PRINT USING "#####.##"; TIMER;
    LOOP WHILE INKEY$=""
    resetpithandler
    LOCATE 24,1: PRINT "Handler successfully restored."
    END

    '========================================================================
    'Following routines are only auxiliary routines
    'for setting up the INT 8 handler

    SUB loadASM (matrix??(), hexcode$)
    'used to convert a string containing hexadecimal code into real code
    length?? = LEN(hexcode$)
    posic?? = VARPTR(matrix??(0))
    DEF SEG = VARSEG(matrix??(0))
    FOR i?? = 1 TO length??-1 STEP 2
    d? = VAL("&H" + MID$(hexcode$, i??, 2))
    POKE posic??, d?
    INCR posic??
    NEXT i??
    DEF SEG
    END SUB

    SUB putintvector (intno%, start??)
    'Lets an interrupt vector point to an array element specified by "start??"
    DEF SEG = 0
    POKEI 4*intno%, VARPTR(start??)
    POKEI 4*intno%+2, VARSEG(start??)
    DEF SEG
    END SUB

    SUB putintaddress (intno%, segm??, offs??)
    'Establishes a segment and offset address as an interrupt vector
    DEF SEG = 0
    POKEI 4*intno%, offs??
    POKEI 4*intno%+2, segm??
    DEF SEG
    END SUB

    SUB getintvector (intno%, segm??, offs??)
    'Returns segment and offset of an interrupt vector
    DEF SEG = 0
    segm?? = peeki(4*intno%+2)
    offs?? = peeki(4*intno%)
    DEF SEG
    END SUB

    '=========================================================================
    'Now following the proper timer routines:

    SUB setpithandler (count??, pithandler??()) PUBLIC
    'Substitutes INT 8 handler
    'pithandler??(28) holds code
    'count?? = new counter value (0 to 65535)
    'Output: pithandler??(0) = LSB, pithandler??(1) = MSB of new tick count
    ' pithandler??(6) = segment, pithandler??(7) = offset of BIOS handler
    ' pithandler??(2) = count??
    cod$ = "00000000000000000000000000000000"
    cod$ = cod$ + "50FA2E83060000012E83160200002EA104002E01060A007302"
    cod$ = cod$ + "CDF6FBCDF5B020E62058CFCF9050495448"
    loadASM pithandler??(), cod$ '(Source code see below)
    getintvector 8, sg??, offs??
    pithandler??(6) = sg??: pithandler??(7) = offs?? 'load variables
    pithandler??(2) = count??
    ! CLI
    putintaddress &HF6, sg??, offs?? 'Relocate original INT 8 handler
    'to INT F6
    putintvector &HF5, pithandler??(26) 'points to IRET instruction
    '(INT F5 will be available to hook
    'a routine which needs to be called
    'at each tick)
    putintvector 8, pithandler??(8) 'Let INT 8 vector point to the entry
    'point of the new handler
    OUT &H43, &H34 'Set clock frequency
    OUT &H40, count?? MOD 256
    OUT &H40, count??\256
    ! STI
    END SUB

    FUNCTION getpit() PUBLIC AS DWORD
    'returns tick count since "setpithandler" was initialized.
    DEF SEG = 0
    tickptr?? = PEEKI(32)
    tickseg?? = PEEKI(34)
    DEF SEG = tickseg??
    getpit = PEEKL (tickptr??-16)
    DEF SEG
    END FUNCTION

    FUNCTION pitcheck() PUBLIC AS WORD
    'Installation check for pithandler
    'Output: 1 = pithandler not installed,
    ' other values: = counter constant in pithandler
    DEF SEG = 0
    s?? = PEEKI(34): o?? = PEEKI(32)
    DEF SEG = s??
    Id$ = PEEK$ (o??+38, 4)
    IF Id$="PITH" THEN pitcheck = PEEKI (o??-12) ELSE pitcheck = 1
    DEF SEG
    END FUNCTION

    SUB pitfreq (freq) PUBLIC
    'Changes timer frequency; freq = frequency in Hz
    IF freq<19 OR freq>2000 THEN EXIT SUB 'because Windows does not accept
    'higher frequencies
    counter?? = &H1234DD\freq
    DEF SEG = 0
    s?? = PEEKI(34): o?? = PEEKI(32)
    DEF SEG = s??
    POKEI o??-12, counter?? 'Set counter constant in interrupt handler
    DEF SEG
    ! CLI
    OUT &H43, &H34 'Set new clock frequency
    OUT &H40, counter?? MOD 256
    OUT &H40, counter??\256
    ! STI
    END SUB

    SUB resetpithandler () PUBLIC
    'retrieves original handler, resets original frequency.
    ! CLI
    getintvector &HF6, sg??, offs??
    putintaddress 8, sg??, offs??
    OUT &H43, &H34 'Restore original clock frequency
    OUT &H40, 0
    OUT &H40, 0
    ! STI
    END SUB

    ==============================
    Source code for INT 8 handler:
    ==============================
    Byte Machine code ASM
    00 00 00 ;var: Tick count (low)
    02 00 00 ;var: Tick count (high)
    04 00 00 ;var: counter constant
    06 00 00 ;var: real clockticks (low)
    08 00 00 ;var: real clockticks (high)
    0A 00 00 ;var: sum for real tick count
    0C 00 00 ;var: BIOS Handler Segment
    0E 00 00 ;var: BIOS Handler Offset

    ;Byte 10h = Routine Entry point
    10 50 PUSH AX
    11 FA CLI
    12 2E CS: ; update Tick count
    13 83 06 00 00 01 ADD WORD [0000], 1
    18 2E CS:
    19 83 16 02 00 00 ADC WORD [0002], 0
    1E 2E CS: ; update real tick counter
    1F A1 04 00 MOV AX, [0004]
    22 2E CS:
    23 01 06 0A 00 ADD WORD [000A], AX
    27 73 02 JNC 002B ; overflow?
    29 CD F6 INT F6 ; yes, then call original INT 8 ; handler (relocated at INT F6)

    2B FB STI
    2C CD F5 INT F5 ; INT F5 can be used to hook further
    ; routines
    2E B0 20 MOV AL, 20 ; acknowledge interrupt
    30 E6 20 OUT 20, AL ; (may not be necessary ?)
    32 58 POP AX
    33 CF IRET
    34 CF IRET ; repeated at an EVEN byte position
    ; so the INT F5 vector can point to
    ; this instruction
    35 90 NOP

    36 50 49 54 48 ;Signature "PITH" for installation check

    (58 bytes)

  • #2
    I bild programs for interfaces that I bild. I need very good timing between the external interface and my programming. But it's hard in windows, and as you know everything is windows now!

    So I finly found a good timeing mothed that will give a very good 1000th / sec accuracy. But it would not work all that good in windows. Then I found WinLib.zip it's fantastic. It allows you to completly shutdown windows so it give your dos program full controll.

    I now have this accuracy in windows every time. I just lock windows up when I need to use the timer and turn it back on when I don't need to use the timer any more. EMail me if you need help with the timers and WinLib support.

    I am very very bizzy but I can try to help you out as much as I can.



    ------------------

    Comment


    • #3
      Thank you, Bobby, for your hint and for WINLIB. I found that the
      function for giving the DOS box "total control" is a simple interrupt
      call which declares certain program sections as "time critical" to
      Windows: Interrupt &H2F, AX = &H1681 for Start Critical Section and AX
      = &H1682 for End Critical Section. According to Ralf Brown's Interrupt
      List, this prevents Windows from task switching during the critical
      section (which should not be too long in time). - The problem is that
      even using this call, the DOS program does not get "absolute" control,
      for example regarding my question, Windows still does NOT allow me
      timer frequencies above 1000Hz.

      I found other suggestions as, for example, using a combination of the
      TIMER value and INP(&H40) (reading the internal timer count directly,
      which should decrease from 65535 to 0 during each timer tick). For
      using this method, you must first determine if you got the high byte or
      the low byte first; then you can use every high byte to get a timer
      resolution of 18.2*256 = 4659 Hz, or use both bytes to get an even
      higher resolution. But there are also several problems I found:

      - It seems that the first byte you get when you first read INP(&H40) is
      neither the high nor the low byte, but garbage (at least on my system).
      - I found that the counter decreased TWICE from 65536 to 0 during each
      timer tick, contrary to every documentation I read. (Maybe somebody can
      explain this to me?)
      - If you use this method, you do not need to reprogram the timer, but
      you can not connect an interrupt to the count. Therefore the program
      must constantly check for the timer value, which uses much CPU time and
      may slow down the rest of the program. On slow systems, it may be
      impossible to check this value as fast as required while performing
      other tasks.

      Hans Ruegg

      Comment


      • #4
        I have not found any code that will do better then 1000Hz.

        The only thing I can recomend is some sort of hardware that will work for you. Make some sort of device that pluges into your ISA or PCI slots that will give you the timer you need.

        Good luck man,
        Bobby

        ------------------
        Bobby Gage II

        Comment


        • #5
          Hans,
          you're always going to have trouble in windows with timing. Since you are using PB/DOS, why don't you run in DOS? You will then have NO problems with timing. I've had accurate timings to 10us without difficulty in DOS using similar methods to you.


          <<the first byte you get when you first read INP(&H40) is neither the high nor the low byte, but garbage>>

          The first byte is the low byte of the timer which increments very quickly so if you want to make sense of it you must read it very quickly. Also, in mode 3 it increments twice on each clock, see below.


          <<I found that the counter decreased TWICE>>

          Your PIT timer is set to the wrong mode. In mode 3 it counts as you describe to give a square wave output on the interrupt line. Change it to mode 2 and it will count as you want it to (65535->0) without any problem and give a pulse output on the interrupt line. Since the PC uses an edge triggerred interrupts then pulse or square wave makes no difference to the interrupts.

          Paul.


          ------------------

          Comment


          • #6
            Thank you, Paul, for your hints - you were right about the timer mode.
            So this would be another possibility for a timer with a higher
            resolution (1/256 of a normal timer tick):

            OUT &H43, &B00110100 'setting timer to mode 2
            '(use this before reading the timer)
            ticks& = peektimer

            FUNCTION peektimer AS LONG
            DEF SEG = 0
            low? = INP(&H40) 'discard low byte
            a& = PEEKL (&H46C)
            SHIFT LEFT a&, 8
            FUNCTION = a&+256-INP(&H40)
            DEF SEG
            END FUNCTION

            As for running in DOS mode, I personally had much trouble when trying to configure my
            system in order to support mouse, soundcard, etc. in DOS mode, and I wonder if other
            users have the same problems. (I am aware this is outside the scope of this subject,
            but I did not yet find a DOS compatible mouse driver on the Windows CD, and even EMM386
            returns an error message when I try to put it into the CONFIG.SYS file ... there are still many
            things I do not understand yet.)
            As I am not a professional programmer, I do not have the time either to search for and read
            the technical information necessary for making good Windows programs. So I am staying
            with DOS which seems to me much easier to understand, but I do not know if Windows users
            will like any programs which suggest them to be run in DOS mode. This is the reason why I am
            trying to find a solution which runs under Windows.

            Hans Ruegg.

            Comment


            • #7
              As for running in DOS mode, I personally had much trouble when trying to configure my system in order to support mouse, soundcard, etc. in DOS mode, and I wonder if other users have the same problems. (I am aware this is outside the scope of this subject, but I did not yet find a DOS compatible mouse driver on the Windows CD, and even EMM386 returns an error message when I try to put it into the CONFIG.SYS file ... there are still many
              things I do not understand yet.)
              I'm not surprised you couldn't find a DOS mouse driver on your Windows CD. You can download v11.0 of the standard Micro$oft MS-DOS Mouse Driver from their web site. For DOS support of your sound card, you will need specific drivers from the card manufacturer. I find it useful to use MEMMAKER to optimise my startup files for DOS.
              If you try to make something idiot-proof, someone will invent a better idiot.

              Comment

              Working...
              X