Chapter 12

Event and error trapping

PowerBASIC contains background processing code, for event trapping, and error trapping, and will generate code in your programs, to make use of this already-working background code.

Event trapping means that you can write interrupt-driven programs, where an interrupt generated by an external event can activate your program, and you can let PowerBASIC do the checking work for you, in its background level.

In between every line of your foreground program, PowerBASIC will generate code to invoke the background portion of its runtime library, which will go to check whatever you have told it to check, like your "watch dog".

This is an alternative to creating your own popup (TSR) programs.

All of the ON <....> statements are event trapping. ON UEVENT gives you the capability to create user-defined event trapping, for unusual types of interrupts. This is another way that PowerBASIC is user-extendible, and customizable.

This makes the compiled output for your program a little bigger, three bytes between every line of your code. Your program will run a little slower, but if you have an 80386 or 80486, you will probably not even notice.

Most commonly, this will be checking the keyboard, ON KEY, to see if a particular key or key combination has been pressed.

You can trap for 11 keys, KEY 15 thru 25, using KEY n CHR$, then with POPUP KEY, you can do more and different keys.

More advanced programmers can utilize the other events.

Event trapping

ON COMMANDS

ON COM

ON ERROR

ON <value> GOSUB

ON <value> GOTO

ON KEY

ON PEN

ON PLAY

ON STRIG

ON TIMER

ON UEVENT

ON COM statement

Purpose: Declares the trap subroutine for serial port events.

Syntax: ON COM({ 1 | 2 | 3 | 4 }) GOSUB { label | line number }

1, 2, 3, or 4 specifies which communications adapter (serial port) is to be read.

label or line number identifies the trap subroutine.

If line number is zero, trapping is disabled.

The ON COM(n) statement has no effect until event trapping is enabled for a given serial port by an appropriate COM(n) ON statement.

Once the COM(n) ON statement has been executed, PowerBASIC checks before the execution of every subsequent program statement to see if a character has arrived at the specified serial port.

If one has, a GOSUB is performed to the designated subroutine.

ON COM(n) GOSUB Label

ON COM(n) GOSUB LineNumber

ON COM(n) GOSUB 0

COM statement

Purpose: Enables or disables trapping of serial communications port events.

Syntax: COM({ 1 | 2 | 3 | 4 }) {ON|OFF|STOP}

1, 2, 3, or 4 is the number of the communications adapter to be trapped.

COM(n) ON enables checking of port n.

After COM(n) ON has been executed, PowerBASIC checks

the indicated communications port before the execution

of each program statement to see if any characters have

arrived.

If so, control passes to the subroutine specified in the

most recently executed ON COM(n) GOSUB statement.

COM(n) OFF disables checking of port n; any activity is ignored.

COM(n) STOP also disables checking at port n, but remembers any activity so that an immediate trap occurs if COM(n) ON is later executed.

ON <VALUE> GOSUB

Purpose: Calls one of several subroutines according to the value of a numeric expression.

Syntax: ON n GOSUB { label | line number } [, { label | line number }]...

n is a numeric expression ranging from 0 to 255, and each label or line number identifies a statement to branch to. When this statement is encountered, the nth label in the list is branched to; a subsequent RETURN returns control to the statement following ON GOSUB. If n is 0 or greater than the number of labels, no branch occurs.

ON <VALUE> GOTO

Purpose: Sends program flow to one of several possible destinations based on the value of a numeric expression.

Syntax: ON n GOTO { label | line number } [, { label | line number }]...

n is a numeric expression ranging from 0 to 255, and each label or line number identifies a statement to branch to. When this statement is encountered, the nth label in the list is branched to. If n is 0 or greater than the number of labels, no branch occurs.

ON KEY statement

Purpose: Declares the trap subroutine to get control if a specific key is pressed.

Syntax: ON KEY(n) GOSUB { label | line number }

n is an integer expression which specifies the key to be trapped

label or line number identifies the first statement of the trap

subroutine.

The ON KEY statement has no effect until event trapping is enabled for a

given key by an appropriate KEY(n) ON statement.

KEY statement

Purpose: Enables or disables trapping for a particular keyboard character

Syntax: KEY(n}) {ON|OFF|STOP}

KEY(n) ON enables checking of port n.

After KEY(n) ON has been executed, PowerBASIC checks

the indicated communications port before the execution

of each program statement to see if the specified key has

been pressed.

If so, control passes to the subroutine specified in the

most recently executed ON KEY(n) GOSUB statement.

KEY(n) OFF disables checking of port n; any activity is ignored.

KEY(n) STOP also disables checking at port n, but remembers any activity

so that an immediate trap occurs if KEY(n) ON is later

executed.

When the specified key has been pressed, an implicit KEY(n) STOP statement

is executed to prevent the trap subroutine from being called recursively

from within itself. The closing RETURN of the handling subroutine

performs an implicit KEY(n) ON unless the trap routine executes an

explicit KEY(n) OFF statement.

*********************> [ p.278 ] what happened to KEY 26 thru 29 ????

Table 12.1

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

Single keys Combination keys

Built-in User-defineable

KEY 1 F1 KEY 15

KEY 2 F2 KEY 16

KEY 3 F3 KEY 17

KEY 4 F4 KEY 18

KEY 5 F5 KEY 19

KEY 6 F6 KEY 20

KEY 7 F7 KEY 21

KEY 8 F8 KEY 22

KEY 9 F9 KEY 23

KEY 10 F10 KEY 24

KEY 11 UpArrow KEY 25

KEY 12 LeftArrow

KEY 13 RightArrow

KEY 14 DownArrow

KEY 30 F11

KEY 31 F12

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

You can custom-define key combinations that will be associated with KEYs 15 thru 25. See Programmer's Guide, Appendix A, Table A.3 and Table A.4 for details.

You can associate your own functions and subroutines with all of these special purpose keys.

Some examples of key combinations that you might want to trap for, to keep control over the machine, no matter what the user tries to do.

Ctrl-Break

Ctrl-C

Ctrl-Alt-Del

rem this one traps for Ctrl-Alt-Del

KEY 15, CHR$(&H0C,&H53,&H03)

ON KEY(15) Gosub TrapWarmBoot

KEY(15) ON

ON PEN STATEMENT

Purpose: Declares the trap subroutine to get control when light-pen activity occurs.

Syntax: ON PEN GOSUB { Label | Line number }

label or line number identifies the first statement of the trap subroutine. The ON PEN statement has no effect until event trapping is enabled for the light pen by PEN ON . Once PEN ON has been executed, PowerBASIC checks before the execution of every subsequent program statement to see if light pen activity has occurred. If it has, a GOSUB is performed to the designated subroutine.

When light pen activity occurs, an implicit PEN STOP statement is executed to prevent the trap subroutine from being called recursively from within itself. The closing RETURN of the handling subroutine performs an implicit PEN ON unless the trap routine executes an explicit PEN OFF statement.

PEN statement

Purpose: Controls the checking of light-pen events.

Syntax: PEN { ON | OFF | STOP }

PEN ON enables trapping of light-pen events by the routine indicated in an ON PEN statement.

PEN OFF disables the PEN function and turns off pen event checking.

PEN STOP turns off pen event trapping, but remembers pen events so that if PEN ON is later issued, a trap occurs immediately.

ON PLAY statement

Purpose: Declares the trap subroutine to get control if the background music buffer note count falls below a certain number.

Syntax: ON PLAY(notecount) GOSUB { label | line number }

label or line number identifies the first statement of the trap subroutine to be called if less than notecount notes remain in the background music buffer. The ON PLAY statement has no effect until event trapping is enabled by PLAY ON . Once PLAY ON has been executed, PowerBASIC checks before the execution of every subsequent program statement to see if notecount has been reached. If it has, a GOSUB is performed to the

designated subroutine. The number of notes in the music buffer must be greater than notecount before PLAY ON is executed, otherwise this event will never be triggered.

PLAY statement

Purpose: Creates music.

Syntax: PLAY string expression

PLAY { ON | OFF | STOP }

string expression is made up of interpreted music notes grouped with other music commands. notes are specified like this: note-letter [{#|+|-}]

See ************* for details on actual use of this command to make music.

The second form of the PLAY statement (PLAY { ON | OFF | STOP }) is used to control event trapping when the number of notes in the background music buffer falls below a certain value. PLAY ON turns on this type of event trapping.

The ON PLAY(n) statement is used to set the number of notes which cause this event to be triggered.

PLAY ON enables a check, before the execution of every statement, to see if the note count has fallen below the trigger value; if it has, program execution is diverted to the routine specified in the ON PLAY(n) statement.

PLAY OFF disables this type of event trapping.

PLAY STOP also disables trapping, but remembers whether the note count has been triggered in the meantime; if a PLAY ON statement is subsequently executed, a trap occurs immediately.

ON STRIG statement

Purpose: Declares the trap subroutine to get control when a joystick button is pressed.

Syntax: ON STRIG(n) GOSUB { label | line number }

label or line number identifies the first statement of the trap subroutine. n is an integer expression indicating the button to be trapped: 0 for button 1 on joystick A, 2 for button 1 on joystick B, 4 for button 2 on joystick A, or 6 for button 2 on joystick B.

The ON STRIG(n) statement has no effect until event trapping is enabled for a joystick button by a STRIG(n) ON statement.

Once a STRIG(n) ON has been executed, PowerBASIC checks before the execution of every subsequent program statement to see if the joystick button has been pressed.

If it has, a GOSUB is performed to the designated subroutine.

When a joystick button trap occurs, an implicit STRIG(n) STOP statement is executed to prevent the trap subroutine from being called recursively from within itself.

The closing RETURN of the handling subroutine performs an implicit STRIG(n) ON unless the trap routine executes an explicit STRIG(n) OFF statement.

STRIG statement

Purpose: Controls joystick button trapping.

Syntax: STRIG { ON | OFF }

STRIG(n) { ON | OFF }

STRIG ON turns on trigger-event trapping so that STRIG function requests can be made. Trapping is performed by the routine specified in an ON STRIG statement. STRIG OFF turns off trigger-event checking.

STRIG(n) ON enables checking for the joystick button specified by n

0 =joystick A, button 1; 2 = joystick B, button 1;

4 = joystick A, button 2; 6 = joystick B, button 2

before every subsequent statement. If the button is pressed, control passes to the statement specified in an appropriate ON STRIG(n) statement.

STRING(n) OFF Joystick event trapping is turned off. Any joystick events after this statement are forgotten.

STRIG function

Purpose: Returns the status of the joystick buttons.

Syntax: y = STRIG(option)

option is an integer expression that controls the result returned:

See ************* for details on actual use of this command to check status of the joystick buttons.

Before making any STRIG function calls, you must enable joystick button checking with the STRIG statement. Use the STICK function to read the position of the joystick itself.

STICK function

Purpose: Returns joystick position information.

Syntax: y = STICK(option)

Returns the information specified by the integer expression option:

0 = returns x coordinate for joystick A

1 = returns y coordinate for joystick A

2 = returns x coordinate for joystick B

3 = returns y coordinate for joystick B

You must execute STICK(0) to trigger the reading process before you can read either dimension of either joystick.

ON TIMER statement

Purpose: Declares the trap subroutine to get control every n seconds.

Syntax: ON TIMER(n) GOSUB { label | line number }

label or line number identifies the first statement of the timer trap routine, and n is an integer expression indicating the number of seconds to wait, from 1 to 86400 (24 hours).

The ON TIMER statement has no effect until time checking is enabled by TIMER ON . Once TIMER ON has been executed, an internal count of seconds begins, and a check is made before the execution of every subsequent program statement to see if the indicated number of seconds have elapsed. If so, a GOSUB is performed to the designated subroutine.

When the subroutine gets control, an implicit TIMER STOP statement is executed to prevent the trap subroutine from being called recursively from within itself.

The closing RETURN of the handling subroutine performs an implicit TIMER ON unless the trap routine executes an explicit TIMER OFF statement.

Thus the seconds count is reset to zero at the end of a timer trap.

TIMER statement

Purpose: Controls trapping of timer events.

Syntax: TIMER { ON | OFF | STOP }

TIMER ON turns on timer-event trapping, so that control is passed to the subroutine specified in a previous ON TIMER(n) statement after n seconds have elapsed. TIMER OFF turns off timer-event trapping. TIMER STOP also turns off timer-event trapping, but timer events are remembered so that if TIMER ON is later issued, a trap occurs immediately.

ON UEVENT statement

Purpose: Declares the subroutine to get control when a user- defined event occurs.

Syntax: ON UEVENT GOSUB { label | line number }

You must enable user-defined event trapping with UEVENT ON .

You must also either $LINK "UEVENT.PBU", or write your own assembly language interrupt routine. You can do the same thing with a POPUP MULTIPLEX, or a POPUP INTERRUPT program.

UEVENT statement

UEVENT ON turns on user event trapping so that control is passed to the subroutine specified in a previous ON UEVENT statement when that user defined event occurs.

UEVENT OFF turns off user-event trapping.

UEVENT STOP also turns off user-event trapping, but timer events are remembered, so that if a UEVENT ON is later issued, a trap occurs immediately.

DECLARE SUB SetUevent()

This procedure is called to inform the run-time library that a user defined event (UEVENT) has occurred. If ON UEVENT is active, the specified subroutine will be executed at the first opportunity.

MISCELLANEOUS COMPILE TIME

EVENT OR ERROR COMMANDS

$EVENT ON will start the compiler generating event-checking code until it sees an $EVENT OFF command.

$EVENT OFF will stop the compiler from generating the code untill it sees an

$EVENT ON command again.

$ERROR BOUNDS will enable generation of array bounds checking code

$ERROR NUMERIC will enable generation of floating point

divide-by-zero and overflow checking code

$ERROR OVERFLOW will enable generation of integer overflow checking code

$ERROR STACK will enable generation of stack overflow checking code upon entry to each subroutine function, or procedure.

$ERROR ALL will enable generation of error-checking code

It is a good idea to compile with $ERROR ALL ON, during testing, then compile with $ERROR ALL OFF, when you are satisfied with the program.

PowerBASIC automatically generates event-trapping code whenever it encounters any instructions that need it.

$EVENT metastatement

If your program contains an event trap of some sort, for example, ON KEY,

ON COM(n), then the compiler default is to generate event checking code. This code causes the program during execution, to check for the event(s) being trapped between every statement.

If your program doesn't do trapping, then $EVENT is automatically set to OFF and no event checking code is generated.

To make your programs smaller, you can gain power over this event-checking code by use of the $EVENT statement.

Include an $EVENT OFF statement at the beginning of your program, and then use an $EVENT ON statement shortly before you actually need event-checking program calls generated, and another $EVENT OFF afterwards, to minimize this type of extra code generation.

COMPILER EVENT-CHECKING

CODE GENERATION

If your program contains an ON UEVENT GOTO statement anywhere, the compiler will generate this event checking code, all throughout your program, even though it is only at risk for events thru a small section of code.

The $EVENT commands give you the power to control whether and where PowerBASIC will generate different types of runtime event-checking code, to minimize the size of the compiled .EXE file output.

$EVENT { ON | OFF | + | - }

These are your choices.

1. event checking code all thru your program.

$EVENT ON

2. No event checking code anywhere in your program.

$EVENT OFF

3. event checking code only thru a frequently called

portion of your program.

$EVENT OFF

...

$EVENT ON

...

Call GetKeyNoEcho

...

$EVENT OFF

...

...

Event-checking code slows execution slightly. $EVENT gives you control over what parts of your program will do event checking. If there is an area where maximum speed is important or you can be certain no events will occur, then bracket this code with $EVENT OFF and $EVENT ON statements.

Example

$LINK UEVENT.PBU

$EVENT ON

'controls compile-time generation of code

UEVENT ON

'controls run-time utilization of generated code

ON UEVENT Gosub Label

UEVENT OFF

'controls run-time utilization of generated code

$EVENT OFF

'controls compile-time generation of code

Label:

rem

rem

return

REM UEVENT.TIP

$IF 0

Here's a good example of how to use UEVENT. In this case, we will hook into the keyboard interrupt, INT 9, and set the UEVENT flag every time any key is pressed. Then, we'll let INT 9 take over like normal.

Every time a key is pressed, the ON UEVENT GOSUB code will execute. This is sort of like ON KEYBOARD GOSUB. For other examples of UEVENT, see the file UEVENT3.BAS, which is like ON PRINTSCREEN GOSUB, or INT1C.BAS, which can be adapted to set the UEVENT flag every clock tick (18 times per second) for a faster timer event than ON TIMER. -Erik

$END IF

DECLARE SUB SetUevent() 'PowerBASIC internal procedure must be declared

'locate our interrupt handler

SG??=CODESEG(INT9) ' INT9 is a label in this program

PR??=CODEPTR(INT9)

'Get the current vector of int 9

CALL GetInterruptVector(&H9,OSG??,OPR??) ' Get the OLD handler pointer

'Poke the old pointer as a hook into our asm routine

'POKE directly into the code segment! There's no other way to "hook"

'the interrupt that I can tell.

DEF SEG = CODESEG(OLDINT9)

POKE$ CODEPTR(OLDINT9),CHR$(&HEA)+MKWRD$(OPR??)+MKWRD$(OSG??)

DEF SEG

'Point the new INT 9 vector to our asm routine which calls SetUEvent

CALL SetInterruptVector(&H9,SG??,PR??)

'========[example]==========================================

' With INT9 now pointing to our own handler, ON UEVENT can be used to

' trigger a subroutine every time a key is pressed.

ON UEVENT GOSUB KeyPress

UEVENT ON

DO:LOOP ' loop forever. We can abort using our UEVENT trap.

' Don't forget to RESTORE THE INTERRUPT VECTOR before exiting the

' program or you'll have to reset (ctrl-alt-del won't even work!)

' In this example, we restore the interrupt and end the program

' when the user presses ESCAPE (see the next part of the program).

'========UEVENT =====================================

KeyPress:

SOUND 1000,.1 ' notice that for every key press, there are more than 1

' occurances of this trap. It's hard to tap a key so fast

' that it only runs this routine once!

A$=INKEY$ ' Get any key that might be in the keyboard buffer

' If ESC then exit: VERY IMPORTANT: You must RESTORE the

' OLD INTERRUPT VECTOR of INT 9 or your machine will lock

' up as soon as you exit to DOS!

IF A$=CHR$(27) THEN

CALL SetInterruptVector(&H9,OSG??,OPR??) 'restore old vector

END

ELSE

IF LEN(A$) THEN PRINT A$; ' print A$ if it's not null

END IF

RETURN

' ****************************************************************

'This is our "HOOK" which intercepts INT9, sets the UEVENT flag, and then

'JUMPS into the original INT 9 handler.

INT9:

! PUSH AX

! PUSH BX

! PUSH CX

! PUSH DX

! PUSH SI

! PUSH DI

! PUSH BP

! PUSH DS

! PUSH ES

! CALL far ptr SetUEvent

! POP ES

! POP DS

! POP BP

! POP DI

! POP SI

! POP DX

! POP CX

! POP BX

! POP AX

OLDINT9:

! DB &HEA ;long jump

! DB 0 ;insert pointer to long jump here

! DB 0 ;these bytes will be modified by the code at the

! DB 255 ;start of this program. 0 0 255 255 is a long jump

! DB 255 ;to the warm boot code in BIOS.

' Our code segment POKE above is what changes these "death" bytes into

' a healthy leap of faith into the default INT 9 handler.

' *********************************************************************

' Support Routines follow

SUB SetInterruptVector(Intr%,Segment??,Offset??)

REG 1,&H2500 + Intr%

REG 4,Offset??

REG 8,Segment??

CALL INTERRUPT &H21

END SUB

SUB GetInterruptVector(Intr%,S??,O??)

REG 1,&H3500 + Intr%

CALL INTERRUPT &H21

S??=REG(9)

O??=REG(2)

END SUB

An internal procedure

DECLARE SUB SetUevent()

This procedure is called to inform the run-time library that a user-defined event (UEVENT) has occurred. If ON UEVENT is active, the specified subroutien will be executed at the first opportunity.

Other ways to trap for events

You can trap for 11 keys, using KEY n CHR$, then with POPUP KEY, you can do more and different keys.

Using popup (TSR) programs, or assembly language programs linked to a particular hot key, or to a hardware or software interrupt.

With KEY n CHR$, and the associated KEY commands, the compiler generates three bytes between every line of your program. This makes your program compiled output bigger.

$EVENT OFF will stop the compiler from generating the code until it sees an $EVENT ON command again.

This file has been created with the evaluation or unregistered copy of
EasyHelp from Eon Solutions
(Tel: UK 0973 209667, Email: eon@cix.compulink.co.uk)