OS-9/OSK Answers! by Joel Mathew Hegberg
September 1993 issue


Due to the great response I received from last month's article and sample program, I'm going to follow-up this month with some code that others have sent in to help improve upon the sample program.

First, Rick Ulland mentioned an easier way to use the mouse values under BASIC09. In my program, I simply programmed in the offsets of the data I wanted from the mouse packet (such as the x- and y-values), and PEEKed into memory. Because the x- and y-values of the mouse are 2-byte integers, it was necessary to compute the mouse cursor data with the formula msb*256+lsb, where msb is the most-significant-byte (the 1st byte of the integer) and lsb is the least-significant-byte (the 2nd byte of the integer).

Rick pointed out a powerful feature under BASIC09... the TYPE statement. Using the TYPE statement, you can set up an area of memory that can address a data structure (such as the mouse packet data) in it's correct form. So, using Rick's suggestion, the msb*256+lsb stuff would turn into Mouse.WRX. I think you will all agree, this is much easier, and I'd like to thank Rick for sharing his insight! I don't believe this will work under OSK, since the mouse cursor data is still stored in 2-byte integers by the windowing system, but BASIC/OSK uses 4-byte integers. My thanks to Rick for sending me the following code:

PROCEDURE GetMouse
TYPE registers=cc,a,b,dp:BYTE; x,y,u:INTEGER
TYPE Mouse=Vld,Act,ToTm:BYTE; X1:INTEGER; TTTo:BYTE; TSSt:INTEGER
; CBSA,CBSB,CCtA,CCtB,TTSA,TTSB,TLSA,TLSB:BYTE; X2,BDX,BDY:INTEGER
; Stat,Res:BYTE; AcX,AcY,WRX,WRY:INTEGER
DIM RegisterSet:registers
DIM callcode:BYTE
PARAM Pt:Mouse

RegisterSet.a:=0
RegisterSet.b:=$89
RegisterSet.x:=ADDR(Pt)
RegisterSet.y:=1
callcode:=$8D
RUN syscall(callcode,RegisterSet)
PRINT "mouse x:";Mouse.WRX
PRINT "mouse y:";Mouse.WRY
END

One of the best features of OS-9 is the signal. Signals allow, among other things, a program to put itself to sleep (so CPU time isn't wasted if there's nothing for the program to do), but awaken if the user presses a key or clicks the mouse button. If a keyboard button is pressed, one signal (value) is sent, but if the mouse button is clicked, another signal is sent, so a program using signals will be able to distinguish what event occurred while it was asleep.

So signals are great, and under C they WORK great, too. Unfortunately, BASIC does not provide any signal functions to the programmer! To me, this is a terrible oversight. So when Ted Jaeger wrote me how he managed to poke in a signal handler for BASIC, I was obviously interested. He sent me several samples of source code using signals in various ways. I'm including two of them here. The first is a program for OSK users. Programmers may want to pay attention to how Ted sets up the OSK syscall registers, since I could not find the information on this in my BASIC manuals. (I finally found out thanks to Chris Hawks. Thanks, Chris!)

The second program from Ted shows how to accomplish signal handling in BASIC09 on the CoCo 3. It is very similar to the sample program found in my column last month. My thanks to Ted for letting me include these in my column for people to learn from! As always, anyone with ideas, suggestions, or source code can send them to me care of this magazine or to my email address "JoelHegberg@delphi.com" on the internet (JOELHEGBERG on Delphi).

Listing of osk_signals program for OSK users:

PROCEDURE osk_signals
(* Written by Ted Jaeger.
(* Aug 18, 1993
(* Getting fancy here.
(* This program assigns signal to mouse
(* then sleeps for a bit
(* while asleep, user can click mouse
(* or press a key on the keyboard
(* to generate a signal.
(* The value of the signal will
(* appear in register d1 as can
(* be seen when register values
(* are displayed.

(* The registers are dumped in response
(* to any signal because an intercept routine
(* is stored in the BASIC variable iceptcode
(* and run when a signal reaches the program.

(* KWindows push-buttons do not move
(* in and out since signals are in use.

BASE 1

(* define a variable for syscall
TYPE registers=d(8),a(8),pc:INTEGER
DIM regs:registers

(* define a variable to capture the mouse package
(* which is returned when the mouse is clicked
(* this is the large package with all mouse information
TYPE rodent=valid,area,ctrl,rsvd,msxmsd,msxlsd,msymsd,msylsd,mssxmsd,mssxlsd,mssymsd,mssylsd,mslftmsd,mslftlsd,msmidmsd,msmidlsd,msrgtmsd,msrgtlsd,mswxmsd,mswxlsd,mswymsd,mswylsd,msswxmsd,msswxlsd,msswymsd,msswylsd,msrv(12):BYTE
DIM msret:rodent

DIM F_Sleep:INTEGER
F_Sleep=$0a

DIM F_Icpt:INTEGER
F_Icpt=$09

DIM MouseSig:INTEGER
MouseSig=$0201

DIM KeySig:INTEGER
DIM key$:string[1]
KeySig=$0202

(* poke in machine language code
(* to dump d0-d7 and a0-a6 to BASIC variable
(* the sequence of opcodes $48, $ff, $ff, etc are
(* the assembler signal handler
TYPE intcepttyp=stcode(4):BYTE; intaddr:INTEGER; rtecode(4):BYTE; intresult(16):INTEGER
DIM iceptcode:intcepttyp
iceptcode.stcode(1)=$48
iceptcode.stcode(2)=$f9
iceptcode.stcode(3)=$ff
iceptcode.stcode(4)=$ff
iceptcode.intaddr=ADDR(iceptcode.intresult)
iceptcode.rtecode(1)=$4e
iceptcode.rtecode(2)=$40
iceptcode.rtecode(3)=$00
iceptcode.rtecode(4)=$1e

(* must be defined as integer, not byte
DIM callcode:INTEGER

DIM signal,z,count:INTEGER

(* let's initialize the variable that will hold the
(* register values when they are dumped.
FOR z=1 TO 16
 iceptcode.intresult(z)=0
NEXT z

ON ERR GOTO 100

(* now we need to tell OSK that a signal handler
(* is installed. If OSK did not know, the program
(* would terminate on receipt of a signal.
callcode=F_Icpt
regs.a(1)=ADDR(iceptcode)
regs.a(7)=ADDR(iceptcode.intresult)
RUN syscall(callcode,regs)

(* screen pausing off
SHELL "tmode nopause"
count=0

PRINT CHR$(12)
PRINT "Click mouse or strike a key to generate a signal!"
PRINT "I'll trap the signal and tell you what happened."
PRINT

(* enter loop where user can generate a signal
LOOP
(* intialize value in register d1
iceptcode.intresult(2)=0

(* limit to 10 passes
count=count+1
EXITIF count=10 THEN
ENDEXIT

(* give mouse a signal
regs.d(1)=0
regs.d(2)=$a4
regs.d(3)=MouseSig
callcode=$8e
RUN syscall(callcode,regs)

(* give keyboard a signal
regs.d(1)=0
regs.d(2)=$1a
regs.d(3)=KeySig
callcode=$8e
RUN syscall(callcode,regs)

(* sleep giving user a chance to generate the signal
callcode=F_Sleep
regs.d(1)=256*4
RUN syscall(callcode,regs)

(* the signal is returned in register d1 so print it
PRINT "Value in d1: "; iceptcode.intresult(2)
signal=iceptcode.intresult(2)
(* since it is known to BASIC, the signal can be
(* examined and the program can respond accordingly
IF signal=MouseSig THEN
 PRINT "You clicked the mouse!"
ELSE
 IF signal=KeySig THEN
  GET #0,key$
  PRINT "You pressed the following key: ";key$
 ENDIF
ENDIF

(* clear out the signals using _ss_release call
regs.d(1)=0
regs.d(2)=$1b
callcode=$8e
RUN syscall(callcode,regs)

ENDLOOP

END

100 errnum=ERR
PRINT "Have error "; errnum

Listing of CoCoMouseKey program for OS-9 users:

PROCEDURE CoCoMouseKey
(* Written by Ted Jaeger
(* Aug 19, 1993
(* For CoCo and BASIC09

(* read the mouse or keyboard via signals.

(* must be run on a graphics screen.
(* type 7 does nicely.

TYPE registers=cc,a,b,dp:BYTE; x,y,u:INTEGER
DIM regs:registers

(* Define a variable to hold the signal handler.
(* The signal handler is actually a short
(* machine language program that dumps the
(* contents of the b register to a BASIC09 variable.
(* The b register contains the signal when
(* sent by a device.
(* If no signal handler is present, the
(* program terminates upon receiving a signal.

TYPE intcepttyp=stbcode:BYTE; intaddr:INTEGER;
  rticode,intresult:BYTE
DIM iceptcode:intcepttyp

(* Now for the actual machine code
iceptcode.stbcode=$F7
iceptcode.intaddr=ADDR(iceptcode)+4
iceptcode.rticode=$3B

(* dimention a variable to capture mouse info
DIM mouse:STRING[32]
DIM a_button,sx,sy:BYTE
DIM callcode:BYTE
DIM key$:STRING[1]

(* text cursor off, echo off
SHELL "display 05 20"
SHELL "tmode -echo"

(* use hires mouse in right joystick port.
(* _ss_gip call
regs.a=0
regs.b=$94
regs.x=$0101
regs.y=$FFFF
callcode=$8E
RUN syscall(callcode,regs)

(* turn mouse on and tell pointer
(* to follow its movements.
(* _ss_mouse call
regs.a=0
regs.b=$89
regs.x=$030A
regs.y=1
callcode=$8E
RUN syscall(callcode,regs)

(* select standard mouse pointer
RUN gfx2("gcset",202,1)

(* Finally, tell os9 to invoke the signal
(* handler when a signal is returned.
(* Here we are telling os9 the address of the
(* signal handler routine and the location of
(* the variable holding the signal.
(* F_Icpt call
callcode=$09
regs.x=ADDR(iceptcode)
regs.u=ADDR(iceptcode)+4
RUN syscall(callcode,regs)

(* set up the screen
PRINT CHR$(12)
PRINT CHR$(2);CHR$(32);CHR$(32);"Press <ENTER> to quit"
PRINT CHR$(2);CHR$(32);CHR$(35);"X value: "
PRINT CHR$(2);CHR$(32);CHR$(36);"Y value: "
PRINT CHR$(2);CHR$(32);CHR$(40);"Keystroke: "

(* now enter the loop
REPEAT

  (* need to initialize the variable that
  (* holds the returned signal.
  iceptcode.intresult=0
  (* and the variable holding any keystroke
  key$=""

  (* tell mouse to return the value 10.
  (* _ss_MsSig call
  callcode=$8E
  regs.a=0
  regs.b=$8A
  regs.x=10
  RUN syscall(callcode,regs)

  (* tell keyboard to return the value 8.
  (* _ss_ssig call
  callcode=$8E
  regs.a=0
  regs.b=$1A
  regs.x=8
  RUN syscall(callcode,regs)

  (* now that each device has a signal
  (* we can go to sleep.
  callcode=$0A
  regs.x=0
  RUN syscall(callcode,regs)

  (* when here, program woke up due to a signal

  (* lets read the mouse if the signal was 10
  (* or read the keyboard if the signal was 8.
  IF iceptcode.intresult=10 THEN
    callcode=$8D
    regs.a=0
    regs.b=$89
    regs.x=ADDR(mouse)
    regs.y=0
    RUN syscall(callcode,regs)
    a_button=PEEK(regs.x+8)
    sx=PEEK(regs.x+24)*256+PEEK(regs.x+25)
    sy=PEEK(regs.x+26)*256+PEEK(regs.x+27)
  ELSE
    IF iceptcode=8 THEN
      GET #0,key$
    ENDIF
  ENDIF

  (* release extra signal
  callcode=$8E
  regs.a=0
  regs.b=$1B
  RUN syscall(callcode,regs)

  IF key$="" THEN
    PRINT CHR$(2);CHR$(41);CHR$(35);"   "
    PRINT CHR$(2);CHR$(41);CHR$(35);sx
    PRINT CHR$(2);CHR$(41);CHR$(36);"   "
    PRINT CHR$(2);CHR$(41);CHR$(36);sy
  ELSE
    PRINT CHR$(2);CHR$(43);CHR$(40);key$
  ENDIF

UNTIL key$=CHR$(13)

(* Keyboard echo back on, and cursor on
SHELL "tmode echo"
SHELL "display 05 21"
END

* THE END *