4 ; Mouse movement constraining
6 ; © 1994-1998 Straylight
9 ;----- Licensing note -------------------------------------------------------
11 ; Constrain is free software; you can redistribute it and/or modify
12 ; it under the terms of the GNU General Public License as published by
13 ; the Free Software Foundation; either version 2, or (at your option)
16 ; Constrain is distributed in the hope that it will be useful,
17 ; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ; GNU General Public License for more details.
21 ; You should have received a copy of the GNU General Public License
22 ; along with Constrain. If not, write to the Free Software Foundation,
23 ; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 ;----- Standard header ------------------------------------------------------
32 ;----- Module header --------------------------------------------------------
34 AREA |!!!Module$$Header|,CODE,READONLY
37 DCD initialise ;Initialisation routine
38 DCD finalise ;Finalisation code
39 DCD 0 ;Service call handler
40 DCD title ;Title pointer
41 DCD version ;The help string
42 DCD 0 ;No keyword table
43 DCD &4A340 ;SWI chunk number
44 DCD swi_handler ;SWI handler code
45 DCD swi_decode ;SWI decode table
46 DCD 0 ;No decoding code needed
48 title DCB "Constrain",0
50 ;----- Initialisation and finalisation --------------------------------------
54 STMFD R13!,{R14} ;Stack link register nicely
56 ; --- Get some workspace ---
58 MOV R0,#6 ;Allocate workspace
59 MOV R3,#ws__wSize ;Make it *this* big
60 SWI XOS_Module ;Get me memory
61 LDMVSFD R13!,{PC} ;Return if it barfed
62 STR R2,[R12] ;Stash the workspace pointer
63 MOV R12,R2 ;Move the pointer across
65 ; --- Do some initialising ---
67 MOV R14,#0 ;No current constraint
68 STR R14,ws__routine ;Store that away
72 LDMFD R13!,{PC}^ ;Return to caller happy
79 MOV R11,R12 ;Keep the private word ptr
80 LDR R12,[R12] ;Find my workspace
82 BL constrain_finish ;Close any constraint
84 ; --- Free my workspace ---
86 MOV R0,#7 ;Free RMA space
87 MOV R2,R12 ;Point to workspace
88 SWI XOS_Module ;Try to free the memory
89 MOV R0,#0 ;Gonna zero the private word
90 STR R0,[R11] ;Then zero it
91 LDMFD R13!,{R11,PC}^ ;A happy bunny
95 ;----- SWI dispatching ------------------------------------------------------
97 swi_decode DCB "Constrain",0
104 swi_handler LDR R12,[R12] ;Get my workspace
105 CMP R11,#(%01-%00)/4 ;Within range?
106 ADDLO PC,PC,R11,LSL#2 ;Yes -- dispatch
107 B %01 ;No -- complain
109 00 B constrain_finish
114 01 ADR R0,noSuchSWI ;Point to the error
115 ORRS PC,R14,#V_flag ;And return with error
118 DCB "Unknown Constrain operation",0
120 ;----- Main code ------------------------------------------------------------
124 ; On entry: R0 == dividend
127 ; On exit: R0 == quotient
130 ; Use: A standard divide routine. Fairly speedy, hopefully.
131 ; The results are always such that
133 ; |quotient| <= |(divisor/dividend)|,
135 ; |remainder| < |divisor|
139 ; quotient * divisor + remainder == dividend
143 STMFD R13!,{R2,R3,R14} ;Save some registers
145 ; --- A note about the method ---
147 ; We use traditional long division, but unroll the loop a
148 ; lot to keep the thing ticking over at a good rate.
150 ; --- First, mess about with the signs ---
152 ANDS R14,R0,#&80000000 ;Get the dividend's sign bit
153 ORR R14,R14,R14,LSR #1 ;Copy -- this is sign of mod
154 RSBNE R0,R0,#0 ;Take absolute value of R0
155 ANDS R3,R1,#&80000000 ;Get the divisor's sign too
156 RSBNE R1,R1,#0 ;Take absolute value of R1
157 EOR R14,R14,R3 ;Calculate sign of quotient
158 MOV R3,R1 ;Look after the divisor
160 ; --- Shift divisor up for long division ---
162 ; We keep shifting the divisor up until it's greater than
163 ; the dividend, and then we skip ahead to the divide section
165 MOV R2,#0 ;Quotient starts off at 0
166 00divide CMP R0,R3,LSL #0
184 ; --- Now we have the shift-down loop ---
186 ; This is where the actual job of dividing is performed.
187 ; We shift the divisor back down until it's back where we
190 17divide CMP R0,R3,LSL #7
192 SUBCS R0,R0,R3,LSL #7
193 16divide CMP R0,R3,LSL #6
195 SUBCS R0,R0,R3,LSL #6
196 15divide CMP R0,R3,LSL #5
198 SUBCS R0,R0,R3,LSL #5
199 14divide CMP R0,R3,LSL #4
201 SUBCS R0,R0,R3,LSL #4
202 13divide CMP R0,R3,LSL #3
204 SUBCS R0,R0,R3,LSL #3
205 12divide CMP R0,R3,LSL #2
207 SUBCS R0,R0,R3,LSL #2
208 11divide CMP R0,R3,LSL #1
210 SUBCS R0,R0,R3,LSL #1
211 10divide CMP R0,R3,LSL #0
213 SUBCS R0,R0,R3,LSL #0
215 CMP R3,R1 ;Have we finished dividing?
216 MOVHI R3,R3,LSR #8 ;No -- shift down a byte
217 BHI %17divide ;And loop round again
219 ; --- Now tidy everything up ---
221 TST R14,#&40000000 ;Is remainder to be negative?
222 RSBNE R1,R0,#0 ;Yes -- negate it
223 MOVEQ R1,R0 ;No -- just copy it nicely
224 TST R14,#&80000000 ;Is quotient to be negative?
225 RSBNE R0,R2,#0 ;Yes -- negate it
226 MOVEQ R0,R2 ;No -- just copy it nicely
228 LDMFD R13!,{R2,R3,PC}^ ;Return to caller
234 ; On entry: R0 == dividend
237 ; On exit: R0 == quotient, rounded to nearest integer
240 ; Use: Calculates a rounded-to-nearest quotient, rather than one
241 ; rounded towards zero, which is what divide returns you.
243 ; The remainder is fiddled during this process, so that the
246 ; quotient * divisor + remainder == dividend
250 ; |remainder| < |divisor|
252 ; still hold (so the remainder's sign may well change).
256 STMFD R13!,{R2,R14} ;Save some registers
257 MOV R2,R0 ;Keep a copy of the dividend
258 CMP R1,#0 ;Is the divisor positive?
259 MOVGE R14,R1 ;Yes -- just copy it
260 RSBLT R14,R1,#0 ;No -- negate it on the way
261 CMP R0,#0 ;Is the dividend positive?
262 ADDGE R0,R0,R14,ASR #1 ;Yes -- add half the divisor
263 SUBLT R0,R0,R14,ASR #1 ;No -- subtract it
264 SUB R2,R2,R0 ;Remember this difference
265 BL divide ;Do the division
266 ADD R1,R1,R2 ;Modify remainder suitably
267 LDMFD R13!,{R2,PC}^ ;Return to caller
273 ; On entry: R0 == value to square-root
275 ; On exit: R0 == the result
277 ; Use: Evaluates the square root of the number given. This routine
278 ; is constructed from the information supplied by David Seal,
279 ; and is *extremely* fast.
284 STMFD R13!,{R1-R4,R14} ;Stack registers
285 MOV R1,#0 ;Result so far
286 MOV R2,#0 ;Current remainder
287 MOV R3,#1 ;A '01' pair
288 MOV R4,#3 ;A nice mask
291 count SETA 0 ;Start the count at 30
293 WHILE count<=28 ;Set up the loop condition
295 AND R14,R4,R0,LSR #30-count
313 c MOV R0,R1 ;Put the result in R0
314 LDMFD R13!,{R1-R4,PC}^ ;Return to caller
318 ; --- constrain_circ ---
320 ; On entry: R0 == x position of circle centre
321 ; R1 == y position of circle centre
322 ; R2 == radius of circle
323 ; R3 == maximum distance below centre allowed
327 ; Use: Constrains the mouse to the given circle outline
331 STMFD R13!,{R0-R2,R14} ;Stack some registers
332 BL constrain_finish ;Clear a current constraint
333 MUL R14,R2,R2 ;Get the radius squared
334 MOV R2,R14 ;Put in back in R2
335 STMIA R12,{R0-R3} ;Store parameter in RMA ws
336 MOV R0,#1 ;Call this often
337 ADR R1,constrain__circ ;Routine to call
338 STR R1,ws__routine ;Remember this pointer
339 MOV R2,R12 ;Pass this R12 value
340 SWI XOS_CallEvery ;Call every 50cs
341 MOV R0,#106 ;Define mouse parameters
342 MOV R1,#&81 ;Pointer 1 -- de-linked
343 SWI XOS_Byte ;Do the OS_Byte call
344 LDMFD R13!,{R0-R2,PC}^ ;Return to caller
346 ; --- constrain_disc ---
348 ; On entry: R0 == x position of circle centre
349 ; R1 == y position of circle centre
350 ; R2 == radius of circle
354 ; Use: Constrains the mouse to the given circle outline
358 STMFD R13!,{R0-R2,R14} ;Stack some registers
359 BL constrain_finish ;Clear a current constraint
360 MUL R14,R2,R2 ;Get the radius squared
361 MOV R2,R14 ;Put in back in R2
362 STMIA R12,{R0-R2} ;Store parameter in RMA ws
363 MOV R0,#1 ;Call this often
364 ADR R1,constrain__disc ;Routine to call
365 STR R1,ws__routine ;Remember this pointer
366 MOV R2,R12 ;Pass this R12 value
367 SWI XOS_CallEvery ;Call every 50cs
368 MOV R0,#106 ;Define mouse parameters
369 MOV R1,#&81 ;Pointer 1 -- de-linked
370 SWI XOS_Byte ;Do the OS_Byte call
371 LDMFD R13!,{R0-R2,PC}^ ;Return to caller
373 ; --- constrain__circ ---
379 ; Use: Called on interrupts to constrin the mouse
382 STMFD R13!,{R0-R11,R14} ;Stack some registers
384 SWI XOS_Mouse ;Get the current mouse pos
385 10 LDMIA R12,{R4-R6,R9} ;Load interesting values
386 MOV R10,#0 ;Temporary flags word
388 ; --- Set R7 == horizontal distance from centre squared ---
390 SUB R14,R0,R4 ;Get distance in R14
391 MUL R7,R14,R14 ;Square it and copy
392 CMP R7,#&10000 ;Is it too big?
393 MOVGT R7,#&10000 ;Yes -- reduce (stop oflow)
394 LDR R8,ws__oldX ;Get the old x position
395 SUB R8,R8,R4 ;Get the relative position
396 EOR R8,R8,R14 ;See if top bit is changed
397 TST R8,#(1<<31) ;Test the top bit
398 ORRNE R10,R10,#(1<<0) ;Set the 'side changed' bit
400 ; --- Set R8 == vertical distance from centre squared ---
402 SUB R11,R5,R9 ;Get actual maximum height
403 MUL R8,R9,R9 ;Get the y distance squared
404 CMP R1,R11 ;Is it high enough?
405 MOVLE R1,R11 ;No -- then raise it
406 SUBLE R7,R6,R8 ;Calculate the corner x pos
407 ORRLE R10,R10,#2 ;Remember we made this bodge
408 SUBS R14,R1,R5 ;Get distance in R14
409 BGE %00constrain__circ ;If in upper quadrants, skip
410 CMP R8,R6 ;Is there a sensible distance
411 BGT %00constrain__circ ;No -- skip this magic bit
412 TST R10,#1 ;Have we changed sides?
413 BEQ %00constrain__circ ;Nope -- skip ahead
414 LDR R0,ws__oldX ;Make sure were on correct sd
415 SUB R7,R6,R8 ;Calculate the corner x pos
416 ORR R10,R10,#2 ;Remember we made this bodge
417 B %01constrain__circ ;Skip next instruction
419 00 MUL R8,R14,R14 ;Square it and copy
420 CMP R8,#&10000 ;Is it too big?
421 MOVGT R8,#&10000 ;Yes -- reduce (stop oflow)
423 01 ADDS R9,R7,R8 ;Dist from centre squared
424 BEQ %90constrain__circ ;If 0, return
426 ; --- Don't allow a diagonal skip ---
428 TST R10,#1 ;Has x changed sides?
429 BEQ %02constrain__circ ;No -- forget this check
430 LDR R14,ws__oldY ;Get the old Y value
431 SUB R3,R14,R5 ;Get distance from centre
432 MOV R2,R1 ;Get the new Y value
433 SUB R2,R2,R5 ;Get the new distance
434 EOR R3,R3,R2 ;Has sign bit changed?
435 TST R3,#(1<<31) ;Test it
436 BEQ %02constrain__circ ;No -- skip ahead
437 MOV R1,R14 ;Use this Y value
438 LDR R0,ws__oldX ;And this X value
439 SUB R14,R0,R4 ;Get the hz distance
440 MUL R7,R14,R14 ;And put the square in R7
441 SUB R14,R1,R5 ;Get the vt distance
442 MUL R8,R14,R14 ;And put the square in R8
443 ORR R10,R10,#2 ;Force a *mouse* update
444 ADDS R9,R7,R8 ;Dist from centre squared
445 BEQ %90constrain__circ ;If 0, return
447 ; --- Calculate corrected relative positions ---
449 02 MOV R2,R0 ;Look after the original...
450 MOV R3,R1 ;... mouse position
452 MOV R1,R9 ;We'll need to divide by this
453 MUL R0,R7,R6 ;Multiply up to give thing
454 BL divRound ;Do the main division
455 BL sqrt ;And take the square root
456 CMP R2,R4 ;Which side of the centre?
457 ADDGE R2,R4,R0 ;This is the x position
458 SUBLT R2,R4,R0 ;This is the x position
460 MOV R1,R9 ;We'll need to divide by this
461 MUL R0,R8,R6 ;Multiply up to give thing
462 BL divRound ;Do the main division
463 BL sqrt ;And take the square root
464 CMP R3,R5 ;Which side of the centre?
465 ADDGE R3,R5,R0 ;This is the y position
466 SUBLT R3,R5,R0 ;This is the y position
468 ; --- Make sure y isn't too low now ---
470 CMP R3,R11 ;Is the y value too low?
471 MOVLT R0,R2 ;Yes -- get the new x and...
472 MOVLT R1,R3 ;... y coordinates
473 BLT %10constrain__circ ;And skip back round (yeuch)
475 ; --- Store the newly modified mouse positions ---
477 STR R2,ws__oldX ;Store the new pointer...
478 STR R3,ws__oldY ;...positions away
480 ; --- Now move the mouse pointer position ---
482 MOV R0,#5 ;OS_Word subreason code
483 ORR R0,R0,R2,LSL #8 ;Move in the x position
484 ORR R0,R0,R3,LSL #24 ;And the bottom of the y
485 MOV R1,R3,LSR #8 ;Get the top of the y
486 STMFD R13!,{R0,R1} ;Save the block on the stack
487 MOV R0,#21 ;OS_Word main reason code
488 MOV R1,R13 ;Point to the new block thing
489 SWI XOS_Word ;Position the mouse pointer
491 ; --- Constrain the *mouse* to the circular band ---
493 SUBS R14,R9,R6 ;f(distance from circumf.)
494 RSBLT R14,R14,#0 ;Make sure it's positive
495 CMP R14,#256 ;Is this within the band?
496 CMPLE R10,#1 ;Make sure we didn't bodge
497 MOVGT R14,#3 ;No -- move mouse position
498 STRGTB R14,[R13,#0] ;Store new subreason code
499 SWIGT XOS_Word ;And move the position
501 ADD R13,R13,#8 ;Reclaim the OS_Word block
502 90 LDMFD R13!,{R0-R11,PC}^ ;Return to caller
506 ; --- constrain__disc ---
512 ; Use: Called on interrupts to constrin the mouse
515 STMFD R13!,{R0-R11,R14} ;Stack some registers
516 SWI XOS_Mouse ;Get the current mouse pos
517 10 LDMIA R12,{R4-R6} ;Load interesting values
518 MOV R10,#0 ;Temporary flags word
520 ; --- Set R7 == horizontal distance from centre squared ---
522 SUB R14,R0,R4 ;Get distance in R14
523 MUL R7,R14,R14 ;Square it and copy
524 CMP R7,#&10000 ;Is it too big?
525 MOVGT R7,#&10000 ;Yes -- reduce (stop oflow)
527 ; --- Set R8 == vertical distance from centre squared ---
529 SUBS R14,R1,R5 ;Get distance in R14
530 00 MUL R8,R14,R14 ;Square it and copy
531 CMP R8,#&10000 ;Is it too big?
532 MOVGT R8,#&10000 ;Yes -- reduce (stop oflow)
534 01 ADDS R9,R7,R8 ;Dist from centre squared
535 BEQ %90constrain__disc ;If 0, return
537 ; --- Calculate corrected relative positions ---
540 02 MOV R2,R0 ;Look after the original...
541 MOV R3,R1 ;... mouse position
543 CMP R6,R9 ;Are we in the circle?
544 BGE %03constrain__disc ;Yes -- just move it then
546 MOV R1,R9 ;We'll need to divide by this
547 MUL R0,R7,R6 ;Multiply up to give thing
548 BL divRound ;Do the main division
549 BL sqrt ;And take the square root
550 CMP R2,R4 ;Which side of the centre?
551 ADDGE R2,R4,R0 ;This is the x position
552 SUBLT R2,R4,R0 ;This is the x position
554 MOV R1,R9 ;We'll need to divide by this
555 MUL R0,R8,R6 ;Multiply up to give thing
556 BL divRound ;Do the main division
557 BL sqrt ;And take the square root
558 CMP R3,R5 ;Which side of the centre?
559 ADDGE R3,R5,R0 ;This is the y position
560 SUBLT R3,R5,R0 ;This is the y position
562 ; --- Store the newly modified mouse positions ---
564 03 STR R2,ws__oldX ;Store the new pointer...
565 STR R3,ws__oldY ;...positions away
567 ; --- Now move the mouse pointer position ---
569 MOV R0,#5 ;OS_Word subreason code
570 ORR R0,R0,R2,LSL #8 ;Move in the x position
571 ORR R0,R0,R3,LSL #24 ;And the bottom of the y
572 MOV R1,R3,LSR #8 ;Get the top of the y
573 STMFD R13!,{R0,R1} ;Save the block on the stack
574 MOV R0,#21 ;OS_Word main reason code
575 MOV R1,R13 ;Point to the new block thing
576 SWI XOS_Word ;Position the mouse pointer
578 ; --- Constrain the *mouse* to the circular band ---
581 MOVLT R14,#3 ;No -- move mouse position
582 STRLTB R14,[R13,#0] ;Store new subreason code
583 SWILT XOS_Word ;And move the position
585 ADD R13,R13,#8 ;Reclaim the OS_Word block
586 90 LDMFD R13!,{R0-R11,PC}^ ;Return to caller
590 ; --- constrain_finish ---
596 ; Use: Finishes a constraining operation
598 constrain_finish ROUT
600 STMFD R13!,{R0-R2,R14} ;Stack some registers
602 ; --- Check whether there's a constrain on ---
604 LDR R0,ws__routine ;Check the routine
605 CMP R0,#0 ;Is there one going?
606 LDMEQFD R13!,{R0-R2,PC}^ ;No -- do nothing then
608 ; --- Remove the routine ---
610 MOV R1,R12 ;Pass this R12 value
611 SWI XOS_RemoveTickerEvent ;Call every 50cs
612 MOV R0,#106 ;Change mouse parameters
613 MOV R1,#&1 ;Pointer 1, linked
614 SWI XOS_Byte ;Do the thing
616 ; --- Clear the routine ---
618 MOV R14,#0 ;Clear the pointer
619 STR R14,ws__routine ;Store that away
620 LDMFD R13!,{R0-R2,PC}^ ;Return to caller
622 ; --- constrain_mousePos ---
626 ; On exit: R0 == x coordinate of pointer
627 ; R1 == y coordinate of pointer
629 ; Use: Returns the current mouse position during a constrain
632 constrain_mousePos ROUT
634 ADR R0,ws__oldX ;Point to the coordinates
635 LDMIA R0,{R0,R1} ;And read them out
636 MOVS PC,R14 ;Return to caller
640 ;----- Workspace layout -----------------------------------------------------
644 ws__wStart # 0 ;The start of the workspace
645 ws__parameters # 16 ;Parameters for the constrain
646 ws__routine # 4 ;The routine to call
647 ws__oldX # 4 ;The old X coordinate
648 ws__oldY # 4 ;The old Y coordinate
650 ws__wSize EQU {VAR}-ws__wStart ;The workspace size
652 ;----- That's all, folks ----------------------------------------------------