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 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 DIM a_button,sx,sy:BYTE DIM callcode:BYTE DIM key$:STRING (* 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