Speed Equalization in
QB

INTRODUCTION

How
often have you begun to run an old QBasic program that you’ve always
wanted to check out or perhaps one you’ve written yourself, only to
discover that the program runs so fast it’s completely unplayable? Or
maybe you’ve programmed a game on one computer, only to find out it’s way too fast or way too slow on a friend’s
machine. It’s annoying to have to adjust a hard-coded delay every time
you run a program that was programmed on a different machine. How do you, as a programmer, ensure that
the speed of your program remains nearly the same regardless of who’s
running it and where?

You’ve
probably already identified the key programming function affecting this
phenomenon: the delay loop. Often
this delay loop consists of an empty FOR/NEXT statement with a large delay
number or, in the case of FreeBasic, a SLEEP statement with the number of
milliseconds for the delay duration. (As you probably already know,
QBasic’s SLEEP statement only accepts numbers of whole seconds to delay.)
This tutorial will present a method for consistently controlling the speed of
any QBasic program regardless of the speed of the cpu
it’s running on. (Ignoring the factor of whether other programs are
running simultaneously on the cpu.)

PART 1 IN BRIEF

The
first part of this tutorial shows how to code a function that simply delays a
specific amount of time consistently, disregarding anything else that might be
going on in the rest of the program. After being passed a variable indicating
the desired number of milliseconds to delay, create a loop that delays that
exact amount of time using SLEEP (FB only), or FOR/NEXT commands.

PART 2 IN BRIEF

The
second part of this tutorial shows how to adjust the delay mechanism to ensure
that all functions of the program perform at the appropriate speed. Determine
the average amount of milliseconds the main program loop or the individual
functions of the program take to execute, and then implement the subroutine
created in part 1 to compensate for the speed of the cpu.

PART 3 IN BRIEF

The final part of this tutorial shows how to ensure
that the process of gauging a machine’s speed doesn’t occur every
time the program is run in order to avoid annoying users with long waits every
time the program begins.

THE GUINEA PIG

For illustration, let’s work with a simple program
that displays a ball bouncing around the screen. Here’s the original
code:

DECLARE SUB changePosition ()

DECLARE SUB dopause ()

DECLARE SUB drawObject ()

SCREEN 13

CLS

'Share all the variables for
simplicity.

DIM SHARED ball(1000) AS INTEGER

DIM SHARED ox AS INTEGER, oy AS INTEGER

DIM SHARED oxl AS INTEGER, oyl AS INTEGER, oc AS INTEGER

DIM SHARED oxdir AS INTEGER, oydir AS
INTEGER

DIM SHARED screenXLimit AS INTEGER, screenYLimit
AS INTEGER

'Note the dimensions of the
current screen mode

screenXLimit = 320

screenYLimit = 200

'Define the starting point
of the ball.

ox = 30

oy = 100

'Define the diameter of the
ball.

oxl = 10

oyl = oxl

'Define the ball’s
starting direction.

oxdir = 1

oydir = 1

'Define the ball’s
color

oc = 4

'Draw and capture the ball
picture.

CIRCLE (ox, oy), (oxl / 2), oc

PAINT (ox, oy), oc

GET (ox - (oxl / 2) - 1, oy - (oyl / 2) - 1)-(ox + (oxl / 2) +
1, oy + (oyl / 2) + 1),
ball

CLS

DO

changePosition

doPause

drawObject

LOOP UNTIL INKEY$ = CHR$(27)

END

SUB changePosition

IF (ox + oxdir)
> (screenXLimit - oxl)
OR (ox + oxdir) < 0 THEN

oxdir = oxdir * -1

END IF

IF (oy
+ oydir) > (screenYLimit
- oyl) OR (oy + oydir) < 0 THEN

oydir = oydir * -1

END IF

ox = ox + oxdir

oy = oy + oydir

END SUB

SUB doPause

DIM delayVar
AS INTEGER

FOR delayVar
= 0 TO 2500000

NEXT

END SUB

SUB drawObject
()

PUT (ox, oy),
ball, PSET

END SUB

PART 1 IN DETAIL

The
goal of the first step is to create a subroutine that can be passed an integer
and delay the program for that number of milliseconds.

FB users
can simply use the SLEEP command with the number of milliseconds. No additional
programming is required.

QB
users can determine the how many empty FOR/NEXT loops per second the cpu can
process by running an empty FOR/NEXT loop for a large number of cycles to
calculate loops per second using either TIMER or TIME$. Then a function can be
called using a FOR/NEXT loop to delay for a given number of milliseconds.
(TIMER could also be used with DO/LOOP to delay for up to hundredths of a
second.) Store the cycles per second in a SHARED variable for later reference.

GAUGE CYCLES PER SECOND

TIMER

‘Beginning
of program

DIM
SHARED cyclesPerSecond AS LONG

SUB
gaugeCyclesPerSecond (cyclesToTest
AS LONG)

DIM
startTime as DOUBLE

DIM
endTime  as DOUBLE

DIM
cycles AS LONG

DIM
secondsPast AS DOUBLE

‘Ensure
that cyclesToTest is a positive number.

IF
cyclesToTest < 1 THEN cyclesToTest
= 100000

‘Run
test.

startTime = TIMER

FOR
cycles = 1 TO cyclesToTest

NEXT

endTime = TIMER

‘Compensate
for a test begun just before 12:00 am system time.

IF startTime > endTime THEN endTime = endTime + 24 * 3600

‘Calculate
duration of test.

secondsPast = endTime
– startTime

IF
secondsPast > 0 THEN

‘Calculate
cycles per second.

cyclesPerSecond = INT(cyclesToTest/secondsPast)

ELSE

‘Cpu
is too fast for test. Set cycles per second to the cyclesToTest
parameter.

cyclesPerSecond = cyclesToTest

END
IF

END
SUB

TIME$

‘Beginning
of program

DIM
SHARED cyclesPerSecond AS LONG

SUB
gaugeCyclesPerSecond (cyclesToTest
AS LONG)

DIM
startTime as STRING, startTimeINT
as LONG

DIM
endTime as STRING, endTimeINT
as LONG

DIM
cycles AS LONG

DIM
secondsPast AS LONG

‘Ensure that cyclesToTest
is a positive number.

IF
cyclesToTest < 1 THEN cyclesToTest
= 100000

‘Run
test.

startTime = TIME$

FOR
cycles = 1 TO cyclesToTest

NEXT

endTime = TIME$

‘Convert
the start and end times to seconds.

startTimeINT = INT(VAL(MID$(startTime, 1, 2)) * 360) + INT(VAL(MID$(startTime,
4, 2)) * 60) + INT(VAL(MID$(startTime, 7, 2)))

endTimeINT = INT(VAL(MID$(endTime, 1, 2)) * 360) + INT(VAL(MID$(endTime,
4, 2)) * 60) + INT(VAL(MID$(endTime, 7, 2)))

‘Compensate for a test begun just before 12:00 am system time.

IF startTimeINT > endTimeINT THEN endTimeINT = endTimeINT + 24 * 3600

‘Calculate
duration of test.

secondsPast = endTimeINT
- startTimeINT

IF
secondsPast > 0 THEN

‘Calculate
cycles per second.

cyclesPerSecond = INT(cyclesToTest/secondsPast)

ELSE

‘Cpu
is too fast for test. Set cycles per second to the cyclesToTest
parameter.

cyclesPerSecond = cyclesToTest

END
IF

END
SUB

THE
DELAY SUBROUTINE

SUB
pleaseDelay (milliseconds AS LONG)

DIM
delayCycles AS LONG

DIM
delay AS LONG

‘Calculate
number of cycles to loop through.

delayCycles = milliseconds *
(cyclesPerSecond/1000)

‘Delay
program.

FOR
delay = 0 TO delayCycles

NEXT

END
SUB

PART 2 IN DETAIL

With
the first step complete, we can now consistently delay a program for a given number
of milliseconds regardless of the speed of the cpu.
The goal of the second step is to determine how long particular sections of our
code should take to run, and implement the appropriate delay accordingly. In a
simple program, it may be possible to employ one delay to compensate for the
main program loop. More complex programs may require implementing multiple
delays. For each section of code that needs a delay, execute the following
steps.

1. CALCULATE THE CODE RUN TIME

Calculate
how many milliseconds the code in question takes to run on the current cpu
using FOR/NEXT with TIMER or TIME$. This step is similar to the gaugeCyclesPerSecond function above. Store the code
execution duration in a SHARED variable for later reference.

TIMER

‘Beginning
of code

DIM
SHARED codeExecuteDuration AS LONG

‘Calculate
run time using TIMER

SUB gaugeCodeDuration (cyclesToTest AS LONG)

DIM startTime AS DOUBLE

DIM endTime AS DOUBLE

DIM cycle AS LONG

‘Decide
how many times to run the code. (Default is 1000 if parameter is 0.)

IF
cyclesToTest < 1 THEN cyclesToTest
= 1000

‘Note
the start time.

startTime = TIMER

‘Run
the code.

FOR
cycle = 1 TO cyclesToTest

‘**Code
to test goes here.**

NEXT

‘Note
the end time.

endTime = TIMER

‘Compensate for a test run just before 12:00 am system time.

IF startTime > endTime THEN endTime = endTime + 24 * 3600

‘Calculate how many seconds the test took to
run.

codeExecuteDuration = endTime – startTime

‘Subtract the amount of time that just running
an empty FOR/NEXT loop would have taken.

‘(cyclesPerSecond
was determined in the gaugeCyclesPerSecond SUB
above.)

codeExecuteDuration = codeExecuteDuration
– (cyclesToTest/cyclesPerSecond)

‘Calculate how long running the code once
takes.

codeExecuteDuration = codeExecuteDuration / cyclesToTest

‘Adjust to milliseconds.

codeExecuteDuration = codeExecuteDuration *
1000

END SUB

TIME$

‘Beginning
of code

DIM
SHARED codeExecuteDuration AS LONG

‘Calculate
run time using TIME$

SUB gaugeCodeDuration (cyclesToTest AS LONG)

DIM startTime AS STRING, startTimeINT AS INTEGER

DIM endTime AS STRING, endTimeINT AS INTEGER

DIM cycle AS LONG

‘Decide
how many times to run the code. (Default is 1000 if parameter is 0.)

IF
cyclesToTest < 1 THEN cyclesToTest
= 1000

‘Note
the start time.

startTime = TIME$

‘Run
the code.

FOR
cycle = 1 TO cyclesToTest

‘**Code
to test goes here.**

NEXT

‘Note
the end time.

endTime = TIME$

‘Convert the start and end times to seconds.

startTimeINT = INT(VAL(MID$(startTime, 1, 2)) * 360) + INT(VAL(MID$(startTime,
4, 2)) * 60) + INT(VAL(MID$(startTime, 7, 2)))

endTimeINT = INT(VAL(MID$(endTime, 1, 2)) * 360) + INT(VAL(MID$(endTime,
4, 2)) * 60) + INT(VAL(MID$(endTime, 7, 2)))

‘Compensate for a test run just before 12:00 am system time.

IF startTimeINT > endTimeINT THEN endTimeINT = endTimeINT + 24 * 3600

‘Calculate how many seconds the test took to
run.

codeExecuteDuration = endTimeINT – startTimeINT

‘Subtract the amount of time that just running
an empty FOR/NEXT loop would have taken.

‘(cyclesPerSecond
was determined in the gaugeCyclesPerSecond SUB
above.)

codeExecuteDuration = codeExecuteDuration
– (cyclesToTest/cyclesPerSecond)

‘Calculate how long running the code once takes.

codeExecuteDuration = codeExecuteDuration / cyclesToTest

‘Adjust to milliseconds.

codeExecuteDuration = codeExecuteDuration *
1000

END SUB

2. DETERMINE THE OPTIMUM RUN TIME

Create
a duration variable for the code in question, and test (through trial and
error) running for various durations until the optimum amount of time is
discovered (using the code in the next step). Initialize the duration variable
at the beginning of the program or in the appropriate initialization
sub/function.

‘Beginning
of code

DIM
SHARED codeDurationTime AS INTEGER

‘Initialize
the duration variable in milliseconds.

‘The
exact value of this variable is usually determined through trial and error.

‘For
example, if the ball in our sample program is traveling too fast, increase the codeDurationTime to slow it down.

codeDurationTime = 450

3. IMPLEMENT THE APPROPRIATE DELAY

Implement
the delay subroutine at the end of each instance of the code that needs to be speed
equalized, checking to make sure the delay is actually
necessary. The amount of the delay should be the codeDurationTime
minus the codeExecuteDuration.

‘Code
Loop

DO

‘**Code**

‘Obviously
the delay can only slow the code down, not speed it up, so if the time it takes
for the code to execute is longer than the time desired, there’s no
reason to run a delay.

IF
codeExecuteDuration < codeDurationTime
THEN pleaseDelay(codeDurationTime
- codeExecuteDuration)

LOOP

Of
course, FB users would replace the pleaseDelay call
with the SLEEP command.

PART 3 IN DETAIL

Now
that the code has been created to automatically equalize our code’s speed
for multiple cpus, a couple additional considerations
should be addressed. One potential negative result of speed equalization is the
long pauses program startup generates by running subroutines that gauge cpu speed. The goal of
the third step is to determine the most efficient way of gauging cpu
speed without annoying users every time the program runs. It is recommended to
run a speed gauge the first time a program is run, but then save the cpu’s speed settings and
afterwards only run the speed gauge if the user chooses to do so.

LIMITING SPEED GAUGE FREQUENCY

DIM SHARED cyclesPerSecond
AS LONG

DIM SHARED codeExecuteDuration
AS LONG

DIM SHARED fileExists AS
INTEGER

DIM fnum AS INTEGER

'Check to see if speed has been gauged yet

SHELL "dir/a/on/b cpuspeed
> t.tmp"

fileExists = 0

frnum = FREEFILE

OPEN "t.tmp" FOR
INPUT AS #frnum

DO WHILE NOT EOF(frnum)
AND fileExists = 0

fileExists = 1

LOOP

CLOSE #frnum

'FreeBasic check for if a file exists:

'fileExists
= LEN(DIR$("cpuspeed"))

IF fileExists = 0 THEN

'Gauge the current cpu speed

LOCATE 10, 6

PRINT "Gauging Machine. Please
Wait..."

gaugeCyclesPerSecond 8999999

gaugeCodeDuration 89999

frnum = FREEFILE

OPEN "cpuspeed" FOR OUTPUT AS #frnum

WRITE
#frnum, cyclesPerSecond, codeExecuteDuration

CLOSE #frnum

ELSE

frnum = FREEFILE

OPEN "cpuspeed" FOR INPUT AS #frnum

INPUT
#frnum, cyclesPerSecond, codeExecuteDuration

CLOSE #frnum

END IF

SHELL "erase t.tmp"

DYNAMIC SPEED CONTROL

In
addition to having the program gauge the cpu only as
much as necessary, it might be helpful in some cases to allow the user to
control how fast the program runs. Code can be added to allow for the user to
adjust the speed of the program by changing the codeDurationTime
variable(s).

TESTING FOR CYCLES VS. SECONDS

It
could be argued that attempting to test a cpu for a
specific number of cycles brings one back to the original issue of problems
with cpus of extremely different speeds. Very slow
computers could take a VERY long time to run the tests and discourage users
from running the program at all. Very fast computers could complete the tests
too quickly to achieve reliable results. This legitimate concern can be
addressed by changing the testing code from running a particular number of test
cycles to testing the code for a particular amount of time while counting how
many loops where completed. Just keep in mind that the extra code it takes to
check whether the time is up will need to be accounted for in the calculations
of codeExecuteDuration variable(s) to maintain
consistent speed control.

PUTTING IT ALL TOGETHER

Here’s
our bouncing ball program implementing the new speed optimized code:

DECLARE SUB changePosition ()

DECLARE SUB dopause ()

DECLARE SUB drawObject ()

'------------------------------------------------

'BEGIN new code

DECLARE SUB gaugeCyclesPerSecond (cyclesToTest
AS LONG)

DECLARE SUB pleaseDelay (milliseconds AS LONG)

DECLARE SUB gaugeCodeDuration (cyclesToTest
AS LONG)

'END new code

'------------------------------------------------

SCREEN 13

CLS

'Share all the variables for
simplicity.

DIM SHARED ball(1000) AS INTEGER

DIM SHARED endProg AS INTEGER, ox AS INTEGER, oy
AS INTEGER

DIM SHARED oxl AS INTEGER, oyl AS INTEGER, oc AS INTEGER

DIM SHARED oxdir AS INTEGER, oydir AS
INTEGER

DIM SHARED screenXLimit AS INTEGER, screenYLimit
AS INTEGER

'------------------------------------------------

'BEGIN new code

DIM SHARED keyInput AS STRING

DIM SHARED cyclesPerSecond AS LONG

DIM SHARED codeExecuteDuration AS LONG

DIM SHARED codeDurationTime AS INTEGER

DIM SHARED initDurationTime AS INTEGER

DIM SHARED fileExists AS INTEGER

DIM frnum
AS INTEGER

DIM displayDuration
AS INTEGER

initDurationTime =
5

codeDurationTime = initDurationTime

displayDuration =
-1

'END new code

'------------------------------------------------

'Note the dimensions of the
current screen mode

screenXLimit = 320

screenYLimit = 200

'Initialize the variable
triggering when the program should end.

endProg = 0

'Define the starting point
of the ball.

ox = 30

oy = 100

'Define the diameter of the
ball

oxl = 10

oyl = oxl

'Define the ball’s
starting direction.

oxdir = 1

oydir = 1

'Define the ball’s
color

oc = 4

'Draw and capture the ball
picture.

CIRCLE (ox, oy), (oxl / 2), oc

PAINT (ox, oy), oc

GET (ox - (oxl / 2) - 1, oy - (oyl / 2) - 1)-(ox + (oxl / 2) +
1, oy + (oyl / 2) + 1),
ball

'------------------------------------------------

'BEGIN new code

checkgauge:

CLS

fileExists = 0

'Qbasic
check for if a file exists:

SHELL "dir/a/on/b cpuspeed > t.tmp"

frnum = FREEFILE

OPEN "t.tmp" FOR INPUT AS #frnum

DO WHILE NOT EOF(frnum) AND fileExists
= 0

fileExists = 1

LOOP

CLOSE #frnum

'FreeBasic check for if a
file exists:

'fileExists = LEN(DIR$("cpuspeed"))

IF fileExists
= 0 THEN

'Gauge the current cpu speed

LOCATE 10, 6

PRINT
"Gauging Machine. Please Wait..."

gaugeCyclesPerSecond 8999999

gaugeCodeDuration 89999

frnum = FREEFILE

OPEN "cpuspeed" FOR OUTPUT AS #frnum

WRITE
#frnum, cyclesPerSecond, codeExecuteDuration

CLOSE #frnum

ELSE

frnum = FREEFILE

OPEN "cpuspeed" FOR INPUT AS #frnum

INPUT
#frnum, cyclesPerSecond, codeExecuteDuration

CLOSE #frnum

END IF

SHELL "erase t.tmp"

'END new code

'------------------------------------------------

CLS

DO

changePosition

'------------------------------------------------

'BEGIN new code

IF codeExecuteDuration
< codeDurationTime THEN pleaseDelay
(codeDurationTime - codeExecuteDuration)

drawObject

IF displayDuration
> 0 THEN

LOCATE 1, 1

PRINT LTRIM$(RTRIM$(STR$(codeDurationTime))) + " milliseconds      "

END IF

'Capture any user keystroke.

keyInput = INKEY$

SELECT CASE UCASE$(keyInput)

'Toggle displaying the codeDurationTime if the 'D' key is pressed.

CASE "D"

displayDuration = displayDuration * -1

LOCATE 1, 1

PRINT SPACE$(40)

'Reset codeDurationTime
to initDurationTime

CASE "R"

codeDurationTime = initDurationTime

'Decrease the code duration
by 1 millisecond if the down arrow key is pressed.

CASE CHR$(255) +
"P"

codeDurationTime = codeDurationTime - 1

IF codeDurationTime < 0 THEN codeDurationTime
= 0

'Increase the code duration
by 1 millisecond if the up arrow key is pressed.

CASE CHR$(255) +
"H"

codeDurationTime = codeDurationTime + 1

'Decrease the code duration
by 1 millisecond if the down arrow key is pressed.

CASE CHR$(0) + "P"

codeDurationTime = codeDurationTime - 1

IF codeDurationTime < 0 THEN codeDurationTime
= 0

'Increase the code duration
by 1 millisecond if the up arrow key is pressed.

CASE CHR$(0) + "H"

codeDurationTime = codeDurationTime + 1

'Exit program if the Escape
key is pressed.

CASE CHR$(27)

endProg = 1

'Re-gauge computer if the
Enter key is pressed.

CASE CHR$(13)

SHELL "erase cpuspeed"

GOTO
checkgauge

END SELECT

'END new code

'------------------------------------------------

LOOP UNTIL endProg = 1

END

SUB changePosition

IF (ox + oxdir)
>= (screenXLimit - oxl -
2) OR (ox + oxdir) < 0 THEN

oxdir = oxdir * -1

END IF

IF (oy
+ oydir) >= (screenYLimit
- oyl - 2) OR (oy + oydir) < 0 THEN

oydir = oydir * -1

END IF

ox = ox + oxdir

oy = oy + oydir

END SUB

SUB drawObject

PUT (ox, oy),
ball, PSET

END SUB

'------------------------------------------------

'BEGIN new code

SUB gaugeCodeDuration
(cyclesToTest AS LONG)

DIM startTime
AS DOUBLE

DIM endTime
AS DOUBLE

DIM cycle AS LONG

DIM secondsPast
AS DOUBLE

'Decide how many times to
run the code. (Default is 1000 if parameter is 0.)

IF cyclesToTest
< 1 THEN cyclesToTest = 1000

'Note the start time.

startTime = TIMER

'Run the code.

FOR cycle = 1 TO cyclesToTest

IF
(ox + oxdir) > (screenXLimit
- oxl) OR (ox + oxdir) <
0 THEN

oxdir = oxdir
* -1

END IF

IF (oy
+ oydir) > (screenYLimit
- oyl) OR (oy + oydir) < 0 THEN

oydir = oydir
* -1

END IF

ox = ox + 0

oy = oy
+ 0

PUT
(ox, oy), ball, AND

'Capture any user keystroke.

keyInput = INKEY$

SELECT CASE UCASE$(keyInput)

'Toggle displaying the codeDurationTime if the
'D' key is pressed.

CASE
"D"

displayDuration = displayDuration

LOCATE 1, 1

PRINT
SPACE$(40)

'Reset codeDurationTime to initDurationTime

CASE
"R"

codeDurationTime = initDurationTime

'Decrease the code duration by 1 millisecond if the down arrow key is
pressed.

CASE
CHR$(255) + "P"

codeDurationTime = codeDurationTime - 0

IF codeDurationTime < 0 THEN codeDurationTime = 0

'Increase the code duration by 1 millisecond if the up arrow key is
pressed.

CASE
CHR$(255) + "H"

codeDurationTime = codeDurationTime + 0

'Decrease the code duration by 1 millisecond if the down arrow key is
pressed.

CASE
CHR$(0) + "P"

codeDurationTime = codeDurationTime - 0

IF codeDurationTime < 0 THEN codeDurationTime = 0

'Increase the code duration by 1 millisecond if the up arrow key is
pressed.

CASE
CHR$(0) + "H"

codeDurationTime = codeDurationTime + 0

'Exit program if the Escape key is pressed.

CASE
CHR$(27)

endProg = 0

'Re-gauge computer if the Enter key is pressed.

CASE
CHR$(13)

END SELECT

NEXT

'Note the end time.

endTime = TIMER

'Compensate for a test run
just before 12:00
am system time.

IF startTime
> endTime THEN endTime =
endTime + 24 * 3600

'Calculate how many seconds
the test took to run.

secondsPast = endTime - startTime

'Subtract the amount of time
that just running an empty FOR/NEXT loop would have taken.

'(cyclesPerSecond was determined in the gaugeCyclesPerSecond SUB above.)

secondsPast = secondsPast - (cyclesToTest / cyclesPerSecond)

'Calculate how long running
the code once takes.

secondsPast = secondsPast / cyclesToTest

'Adjust to milliseconds.

codeExecuteDuration
= secondsPast * 1000

END SUB

SUB gaugeCyclesPerSecond
(cyclesToTest AS LONG)

DIM
startTime AS DOUBLE

DIM
endTime  AS DOUBLE

DIM
cycles AS LONG

DIM
secondsPast AS DOUBLE

'Ensure
that cyclesToTest is a positive number.

IF
cyclesToTest < 1 THEN cyclesToTest
= 100000

'Run
test.

startTime = TIMER

FOR
cycles = 1 TO cyclesToTest

NEXT

endTime = TIMER

'Compensate
for a test begun just before 12:00 am system time.

IF
startTime > endTime THEN
endTime = endTime + 24 *
3600

'Calculate
duration of test.

secondsPast = endTime
- startTime

IF
secondsPast > 0 THEN

'Calculate
cycles per second.

cyclesPerSecond = INT(cyclesToTest / secondsPast)

ELSE

'Cpu
is too fast for test. Set cycles per second to the cyclesToTest
parameter.

cyclesPerSecond = cyclesToTest

END IF

END SUB

SUB pleaseDelay
(milliseconds AS LONG)

DIM
delayCycles AS LONG

DIM
delay AS LONG

'Calculate
number of cycles to loop through.

delayCycles = milliseconds * (cyclesPerSecond / 1000)

'Delay
program.

FOR
delay = 0 TO delayCycles

NEXT

END SUB

'END new code

'------------------------------------------------