WaitTimer

Post your FreeBASIC source, examples, tips and tricks here. Please don’t post code without including an explanation.
Post Reply
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

WaitTimer

Post by D.J.Peters »

Must be tested / tuned more. I'm view FiFa WM ATM and drinking some beer.

Joshy

Code: Select all

private function ReadTSC as longint
  asm RDTSC
  asm mov [function+0],eax
  asm mov [function+4],edx
end function
dim shared as ulongint TPC ' ticks per command
dim shared as ulongint TPS ' ticks per second
dim shared as ulongint TPM ' ticks per milli second mS
dim shared as ulongint TPU ' ticks per micro second uS

sub calibrate
  dim as ulongint t1,t2,t
  'print "calibrate time step counter"
  ReadTSC()
  ReadTSC()
  ReadTSC()
  for i as integer=1 to 1000
    t1 = ReadTSC()
    t2 = ReadTSC()
    t+= (t2-t1)
  next
  TPC=t\2000 ' ticks per one ReadTSC()
  t=0
  for i as integer=1 to 10
    t1 = ReadTSC()
    sleep 100,1
    t2 = ReadTSC()
    t+= (t2-t1)
  next
  t-=TPC*20  ' subtract time of 20 calls to ReadTSC
  t\=10               ' ticks per 100 mS
  TPS = t*10          ' ticks per second
  TPM = TPS \    1000 ' ticks per mS
  TPU = TPS \ 1000000 ' ticks per uS

  'print "TPC command    = " & TPC
  'print "TPS second     = " & TPS
  'print "TPM milli sec. = " & TPM
  'print "TPU micro sec. = " & TPU
end sub

sub WaitTimer(byval WaitCount as longint)
  static as longint oldTickCount=0
  if oldTickCount=0 then 
    calibrate
    oldTickCount=ReadTSC()
  end if
  WaitCount*=TPM
  OldTickCount+=WaitCount
  dim as longint TickCount=ReadTSC()
  if (TickCount > OldTickCount) then
    OldTickCount = TickCount
  end if

  dim as longint Dif = OldTickCount - TickCount
  if dif<=0 then return

  while dif > 0
    if dif>(TPM*15) then sleep 15,1
    TickCount=ReadTSC()
    dif = OldTickCount - TickCount
  wend
  OldTickCount=TickCount
end sub
' short test
dim as integer xPos=-100
dim as integer bpp
screeninfo ,,bpp
screenres 800,600,bpp
while inkey=""
  ScreenLock:cls
  circle (xpos,300),100,RGB(255,0,0),,,,F
  ScreenUnlock
  xpos+=1:if xpos>900 then xpos=-100
  WaitTimer(20) ' 50 Hz
wend
Last edited by D.J.Peters on Jul 08, 2014 21:47, edited 5 times in total.
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

Re: WaitTimer

Post by D.J.Peters »

You can try NAKED also.

Joshy

Code: Select all

private function ReadTSC naked as longint
  asm RDTSC
  asm ret
end function
MichaelW
Posts: 3500
Joined: May 16, 2006 22:34
Location: USA

Re: WaitTimer

Post by MichaelW »

For multi-core processors Microsoft recommends against using RDTSC to generate real-time timings, see Game Timing and Multicore Processors.
Running this code:

Code: Select all

#include "windows.bi"

function TSC naked() as ulongint
    asm
        rdtsc
        ret
    end asm
end function

dim as ulongint count, sum, t1, t2
dim as double t

dim as DWORD_PTR processAffinityMask, systemAffinityMask

''--------------------------------------------
'' Both functions return non-zero on success.
''--------------------------------------------

print GetProcessAffinityMask( GetCurrentProcess(), _
                              @processAffinityMask, _
                              @systemAffinityMask )

print bin(systemAffinityMask,8)

print SetProcessAffinityMask( GetCurrentProcess(), 1 )

do
    t1 = TSC()
    t = timer + .001
    do
    loop until timer > t
    t2 = TSC()
    sum += t2-t1
    count += 1
    locate 6,1
    print sum \ count
    if multikey(1) then exit do
loop

sleep
On my CORE-i3 (2 cores, no HT) I can see no problems and the mean count does converge on a value close to the nominal clock speed of the processor / 1000. But with the retail-box processor fan and heat sink, and a well-ventilated case, I can't see this simple code overheating the core it's running on, so I doubt that there is any sort of thermal throttling happening.

The article fails to make clear that the problems are with real-time timings only, and that using RDTSC to generate timings in units of clock cycles is as valid as it ever was, assuming that you restrict the timed process to running on a single core.
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: WaitTimer

Post by Munair »

Changing:

Code: Select all

private function ReadTSC as longint
  asm RDTSC
  asm mov [function+0],eax
  asm mov [function+4],edx
end function
to:

Code: Select all

private function ReadTSC as longint
  asm RDTSC
  asm mov [function+0],rax
  asm mov [function+4],rdx
end function
makes it run smoothly on 64 bits. It does here on Linux. ;)
jj2007
Posts: 2326
Joined: Oct 23, 2016 15:28
Location: Roma, Italia
Contact:

Re: WaitTimer

Post by jj2007 »

I wonder where mov [function+4],rdx is writing to...
Munair
Posts: 1286
Joined: Oct 19, 2017 15:00
Location: Netherlands
Contact:

Re: WaitTimer

Post by Munair »

jj2007 wrote:I wonder where mov [function+4],rdx is writing to...
Me too, but I'm not all that much at home with ASM. Long ago I wrote some screen related routines in DOS to speed up text interface manipulation, but that was 16 bits and pretty straight forward with only two-byte integers. I became too lazy catching up with 32 and 64 bit asm because of the great compilers doing the work for us. ;)
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: WaitTimer

Post by MrSwiss »

Well, the RDX stuff, isn't needed at all (only EDX, in 32-bit), in 64-bit:

Code: Select all

Private Function ReadTSC() As LongInt
  asm RDTSC
  asm mov [function], rax
End Function

Dim As LongInt ct = ReadTSC, tt = 0

Print "Time: "; Str(ct)
tt = ct + 1000

While ReadTSC < tt : Wend

Print : Print "Time: "; Str(tt);
Print Tab(20); "Timediff: "; Str(tt - ct)

Sleep
The use of RDX, is probably (sooner than later) going, to crash a program.
Post Reply