[net.micro.atari16] Interrupt running programs

mdoerr@uklirb.UUCP (10/10/86)

Ask

> Challenge:  can anybody devise a way to stop a running program that does
> not do any I/O (i.e. does not respond to ctrl-C), but WITHOUT rebooting!?
> (I currently embed calls to keybrk() here and there in my loops, where
> keybrk() is just a call to GEMDOS to see if ctrl-C was pressed, and an exit()
> if it was... - that works but is ugly!)  - I would like some program that
> responds to an interrupt (say the alt-Help) and stops the currently running
> application, in a clean way (no crashes or reboots, ability to do it
> repeatedly in a session).  I would like this ditty to be a separate program
> that you run once and then it sits resident in RAM.  I have tried such
> a thing, with the interrupt handler doing an RTE to itself and then calling
> gemdos(0) - but I get mysterious crashes after a few uses!  Any ideas?
>
> - Moshe Braner

and thou shall receive:

WATCHER is another goodie from the Happy-Computer PD-Disk (remember MONST).
It should work as follows:
If your program gets stuck in an endless loop
	then press 3 times CTRL-B and once CTRL-C

The executable has been UUENCODED on the ST. The source is for assembly
language adventurers.

As an additional bonus I've thrown in TBUG.PRG. Place multiple copies of it
into the AUTO folder of your best friend and watch his/her eyes fall out.
Have fun, but don't flame me!

Michael Doerr
Uni. of Kaiserslautern (Germany)
uucp: ...!seismo!unido!uklirb!mdoerr

----------------- This is NO shell archive ------------------
begin 777 WATCHER.PRG
M8!H   %2    "@   !@                  "!O  0L/    0#<J  ,W*@ 
M%-RH !PCQ@   5P_/  !/SP #DY.6(\CP    6 O/    $H_/  F3DY<CT)G
M+SD   %</SP ,4Y!,#D   14Y4@@>0  !%9"04JP$ !G"%A!LD!F]$YU(_D 
M  !P   !9"/\    B    '!%\!  )+P   "63G4CSP   6@O.0   61.=2!Y
M   !:# 0P'P@ &<"3G5"N0   7 @>0   6 B:   ,B@ !#0H  8V*  (. ,Z
M K9"9@).=6X$. %91 PQ  )0 V8&4KD   %P6$6X16SLMD)N&$)%. ,,,0 "
M4 -F!E*Y   !<%A%N$5L[ RY     P   7!L DYU('D   %H(^@  @   6PA
M?    2@  DYU+SD   %L2.?@X"\\   !4C\\  I.05R/2CD   %39P9,WP<'
I3G5"9TY!"                !@2!A N!@X(!@@0!BX<$ H(!@H*#@!,
 
end
---------------  Cut  Here  ---------------------------------
begin 777 TBUG.PRG
M8!H   +V                             $*G/SP ($Y!7(\CP    @A(
MY__^82),WW__+SD   ((/SP ($Y!7(\_/   +SP   /H/SP ,4Y!('D   16
M(^@ !    @Q#^0   (8A20 $0?D   )*(\@   (^0?D   *&(\@   )",#D 
M  2\ H    #^(\    (8(_P        "1DYU0H 0.0#_@@'A@- Y /^" ^& 
M(\    (<(#D   (<T+D   (8($!#^0   B!A  $B(#D   (<T+D   (8($ B
M>0   CXD>0   D)A  #H80 !($* $#D _X(###D P #_@@-L$-"\    0+ Y
M /^"!VSX8!RP.0#_@@=O[M"\    0,"\    _[ Y /^"!VSX(#D   (<T+D 
M  (8($!#^0   B!A  "Z(#D   (<T+D   (8($!2N0   D9*: * 9@92N0  
M D8,N0    @   )&;UY#^0   FA%^0   J2S^0   CYF#$/Y   "2D7Y   "
MAB/)   "/B/*   "0B/\         D8&N0   %    (8#+D  #_X   "&&T2
M,#D   2\ H    #^(\    (83G5P#C(:1E"#4$90,A PF8-0T?P   !04<C_
MZDYU< XRT-'\    4%'(__9.=7 .,)G1_    %!1R/_V3G4@>0   @RQ_   
M  !G DZ03G4                                                 
M                                         P?F?;R9F!6I$8MUKI&(
M%:@1BW_^C; 'X /  8#  &?@/;X9F96HT8AUKA&)%:C1B'_^#;$'X /  8  
M P?F?_R?^!_Y'_M__I_X'_@?^W_^C_ 'X /  8#  &?@/_X?^9_XW_A__A_Y
M'_C?^'_^#_$'X /  8 H0RD@3RX@2F]P<&EC:"P@06T@2&]N:6=B;&5E:R Q
M-"P@,S,P,"!"<F%U;G-C:'=E:6<     #! F!@H&!@82"AH&!@@*!@@&3 8(
2"@8(# H(!@8(!@8&"@H*%$0 
 
end
--------------------- Cut Once More -------------------------

*  watcher.s
*  written by ww&js on 13.2.86/14.2.86

* Dieses Programm ermoeglicht es, Programme mit Endlosschleifen
* abzubrechen.

gemdos    equ   1        * System-Aufruf Konstanten
xbios     equ  14
print     equ $09        * GEMDOS Systemaufrufe
keep      equ $31
readline  equ $0a
iorec     equ  14        * XBIOS Systemaufrufe
super     equ  38
ibuf      equ   0        * Offsets of struct iorec
isize     equ   4
ihd       equ   6
itail     equ   8
ctrl_b    equ   2        * Control-B
_vblqueue equ $456       * Systemaddresen: VBL Adressen
nvbls     equ $454       * Anzahl VBL-Routinen
level_4   equ $70        * Level 4 Interruptvector

     .bss
laenge    .ds.l     1
iorecp    .ds.l     1
oldlevel  .ds.l     1
merksp    .ds.l     1
merkpc    .ds.l     1
count     .ds.l     1

* Berechne Groesse des Programms:
     .text
     move.l    4(sp),a0       * basepage Adresse
     move.l    #$100,d6       * basepage Groesse
     add.l     12(a0),d6      * text Groesse
     add.l     20(a0),d6      * data Groesse
     add.l     28(a0),d6      * bss Groesse
     move.l    d6,laenge      * abspeichern

     move      #1,-(sp)       * XBIOS iorec(1) (Tastaturpuffer)
     move      #iorec,-(sp)   * (liefert den Beschreibungsblock)
     trap      #xbios
     addq.l    #4,sp
     move.l    d0,iorecp      * Adresse merken

     move.l    #main,-(sp)    * Routine main im Supervisormode ...
     move      #super,-(sp)   * ...laufen lassen
     trap      #xbios
     addq.l    #6,sp

     clr       -(sp)          * GEMDOS keep(laenge,0)
     move.l    laenge,-(sp)   * (Programm wird beendet, aber...
     move      #keep,-(sp)    * ...im Speicher resident gehalten)
     trap      #gemdos


* Routine vbl in Liste der VBL-Routinen eintragen
main
     move      nvbls,d0       * Anzahl VBL-Routinen
     lsl       #2,d0
     move.l    _vblqueue,a0   * Adr. der VBL-Routinen
     clr       d1
loop1
     tst.l     (a0,d1)        * Routine frei?
     beq       free           * ja, freier Platz gefunden
     addq      #4,d1          * naechste Routine
     cmp       d0,d1          * Ende?
     bne       loop1
     rts                      * keine freie Routine gefunden, return

* freie VBL-Routine gefunden; Interruptvektor und eigene Routine
* eintragen:
free
     move.l    level_4,oldlevel    * bisherigen Int.-Vektor merken
     move.l    #newlevel,level_4   * neuen Int.-Vektor eintragen

     lea       (a0,d1),a2     * Adr. der freien VBL-Routine
     move.l    #vbl,(a2)      * eigene VBL-Routine eintragen

     rts                      * fertig

* eigene Level 4 Interruptbehandlung (VBL Interrupt)
newlevel
     move.l    sp,merksp      * Stackpointer merken
     move.l    oldlevel,-(sp) * zur System-Interruptbehandlung springen
     rts

* eigene VBL-Routine
vbl  move.l    merksp,a0      * Interrupt-Stackpointer laden
     move      (a0),d0        * SSR des unterbrochenen Programms
     and       #$2000,d0      * User/Supervisor Bit testen
     beq       continue       * nicht gesetzt -> User-Mode
     rts                      * unterbrochenes Programm war im Super-
*                             * visor Mode; nicht abbrechen

* unterbrochenes Programm ist im User-Mode; XBIOS Tastaturpuffer
* nach CTRL-B durchsuchen:
continue
     clr.l     count          * Zaehler init.
     move.l    iorecp,a0      * Adr. Pufferbeschreibungsblock
     move.l    ibuf(a0),a1    * Adr. Tastaturpuffer
     move      isize(a0),d1   * Puffergroesse (in bytes)
     move      ihd(a0),d2     * Ringpuffer Anfang (Index)
     move      itail(a0),d3   * Ringpuffer Ende (Index)

     move      d3,d4          * D4: Ende fuer erste Schleife
     move      d2,d5          * D5: Laufvariable
     cmp       d2,d3          * Testen ob Puffer leer
     bne       cont2          * nicht leer
     rts                      * leer;
cont2
     bgt       nowrap         * nur eine Schleife
     move      d1,d4          * 2 Schleifen; erste bis Puffergroesse...
     subq      #4,d4          * ...-4
nowrap
     cmp.b     #ctrl_b,3(a1,d5) * CTRL-B im Puffer?
     bne       cont3          * nein
     addq.l    #1,count       * zaehlen
cont3
     addq      #4,d5          * naechster Index
     cmp       d5,d4          * Ende erreicht?
     bge       nowrap         * Nein, weiter

     cmp       d2,d3          * Test ob 2 Schleifen
     bgt       ende           * nein, fertig mit Durchsuchen
     clr       d5             * Schleife 2 von 0 bis...
     move      d3,d4          * ...Ende Index
loop3
     cmp.b     #ctrl_b,3(a1,d5) * Schleife wie oben
     bne       cont4
     addq.l    #1,count
cont4
     addq      #4,d5
     cmp       d5,d4
     bge       loop3          * Schleifenende
ende
     cmp.l     #3,count       * mindestens 3 mal CTRL-B gefunden?
     bge       kill           * ja, kill'em all
     rts                      * sonst normaler Ruecksprung

* Ruecksprung des unterbrochenen Programms umleiten:
kill
     move.l    merksp,a0      * Interrupt Stackpointer
     move.l    2(a0),merkpc   * Original-Ruecksprungadresse merken
     move.l    #abort,2(a0)   * neue Ruecksprungadr. dafuer eintragen
     rts                      * Ende VBL-Routine

* An diese Stelle wird der Ruecksprung des unterbrochenen Programms
* umgeleitet, falls 3 mal CTRL-B im Tastaturpuffer gefunden wurde
abort
     move.l    merkpc,-(sp)   * Original-Ruecksprungadresse
     movem.l   d0-d2/a0-a2,-(sp) * Register retten
     move.l    #ibuff,-(sp)   * Eingabezeile lesen
     move      #readline,-(sp)
     trap      #gemdos
     addq.l    #6,sp

     tst.b     ibuff+1        * Eingabe da?
     beq       exit           * Nein, vermutlich EOF bei Input Redirection

     movem.l   (sp)+,d0-d2/a0-a2 * ansonsten Programm fortsetzen
     rts

exit
     clr       -(sp)
     trap      #gemdos
     .data
ibuff
     .dc.b     8,0
     .ds.b     8
     .end

------------------ This Is The End ----------------------------

braner@batcomputer.TN.CORNELL.EDU (braner) (10/18/86)

[]

Finally, it seems I am on the right track towards writing a program
that will stop other programs.  The following, after running, sits
in RAM invisibly, until you hold down Control, Alternate and BOTH
shift keys and press another key (e.g. Return).  The program in
progress at the time will be terminated.

One problem I had to solve was how to avoid OS calls from
inside the interrupt handler.  I did that by using direct
peeking of the shift keys status, and by doing the actual
termination of the program AFTER the RTE.

WARNING:  This program uses some addresses that are essential
but not official.  I found them out by snooping around in the
ROM code.  They may be changed in future versions of TOS.
Atari will send their death squads after you if you use these
addresses in a commercial program!

Here is the heart of the routine, in commented assembly syntax:

asm {
/*
 * call supexec() to install our thing
 */
	PEA	install(PC)
	MOVE.W	#38,-(A7)
	TRAP	#14
	ADDQ.L	#6,A7
/*
 * terminate and stay resident
 */
	CLR.W	-(A7)
	MOVE.L	#0x00000200,-(A7)
	MOVE.W	#0x31,-(A7)
	TRAP	#1
install:
/*
 * install our keyboard interrupt handler
 * (has to be done in supervisor mode)
 */
	PEA	handler(PC)
	MOVE.L	(A7)+,0x118	/* pointer to kbd intrpt subr */
	RTS
handler:
/*
 * check keyboard shift status
 */
	CMPI.B	#0x0F,0xE1B	/* shift status variable */
	BCS.S	normal
/*
 * overwrite return address with our posthandler
 */
	PEA	killer(PC)
	MOVE.L	(A7),6(A7)
	ADDQ.L	#4,A7
normal:
/*
 * go do the usual thing, usually return to interrupted program
 */
	JMP	0xFC281C	/* where $118 pointed to */
killer:
/*
 * RTE to here to terminate program
 */
	CLR.L	-(A7)
	TRAP	#1
/*
 * should not come back, but: 6 bombs if you do
 */
	CHK	#-1,D0
}

Here is a hex dump of the complete program (140 bytes):
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
60 1A 00 00 00 70 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 FF FF 2A 4F 2A 6D
00 04 20 2D 00 0C D0 AD 00 14 D0 AD 00 1C D0 BC
00 00 01 00 2F 00 2F 0D 3F 00 3F 3C 00 4A 4E 41
DF FC 00 00 00 0C 48 7A 00 1A 4E 71 3F 3C 00 26
4E 4E 5C 8F 42 67 2F 3C 00 00 02 00 3F 3C 00 31
4E 41 48 7A 00 08 21 DF 01 18 4E 75 0C 38 00 0F
0E 1B 65 0A 48 7A 00 0E 2F 57 00 06 58 8F 4E F9
00 FC 28 1C 42 A7 4E 41 41 BC FF FF
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Please try it out and comment on its performance under various
circumstances.  Remember: the object is a clean break, with no
after effects, avoiding RESETs when possible.

- Moshe Braner

<braner@amvax.tn.cornell.edu>

P.S. I have not tried "Watcher" yet - it may be similar or better?

braner@batcomputer.TN.CORNELL.EDU (braner) (10/19/86)

[]

Recently I posted here a program that, after running it once,
was supposed to kill any program in progress upon pressing
Ctrl-Alternate-both_Shifts-Return.  Turns out that the program
did not work outside of micro-C-Shell, and poorly even there!

I took a careful look at WATCHER.S (also posted here recently) and
noticed there that a kill is NOT done if the interrupted program
was in supervisor mode.  I don't know why, maybe because that
signals that another interrupt handler (or TRAP!) is in progress.
Incorporating that idea, jumping to the last part of the ROM
bomb handler to do the termination, and some streamlining yielded
the following program which seems to work a lot better.
(you have to press Ctrl-Alt-both_Shifts-Return several times
sometimes, probably in order to catch it in user mode...)

Mystery remaining:

	When it terminates, two bombs are displayed.  WHY?


WARNING:

	This program uses some addresses that are essential
	but not official.  I found them out by snooping around in the
	ROM code.  They may be changed in future versions of TOS.
	Atari will send their death squads after you if you use these
	addresses in a commercial program!


Here is the heart of the routine, in commented assembly syntax:

asm {
/*
 * call supexec() to install our thing
 */
	PEA	install(PC)
	MOVE.W	#38,-(A7)
	TRAP	#14
	ADDQ.L	#6,A7
/*
 * terminate and stay resident
 */
	CLR.W	-(A7)
	MOVE.L	#0x00000200,-(A7)
	MOVE.W	#0x31,-(A7)
	TRAP	#1
install:
/*
 * install our keyboard interrupt handler
 * (has to be done in supervisor mode)
 */
	PEA	handler(PC)
	MOVE.L	(A7)+,0x118	/* pointer to kbd intrpt subr */
	RTS
handler:
/*
 * check mode of interrupted program and keyboard shift status
 */
	BTST	#5,(A7)		/* check for super mode */
	BNE.S	normal
	CMPI.B	#0x0F,0xE1B	/* kbd state variable */
	BCS.S	normal
/*
 * overwrite return address with fatal address
 */
	MOVE.L	#0xFC0A5A,2(A7)	/* near end of _term */
normal:
/*
 * go do the usual thing, usually return to interrupted program
 */
	JMP	0xFC281C	/* where $118 pointed to */
}


Here is a hex dump of the complete program (92 bytes):

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
60 1A 00 00 00 70 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 FF FF 48 7A 00 18
3F 3C 00 26 4E 4E 5C 8F 42 67 2F 3C 00 00 02 00
3F 3C 00 31 4E 41 48 7A 00 08 21 DF 01 18 4E 75
08 17 00 05 66 10 0C 38 00 0F 0E 1B 65 08 2F 7C
00 FC 0A 5A 00 02 4E F9 00 FC 28 1C
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Any comments/experiences welcome!

- Moshe Braner

<braner@amvax.tn.cornell.edu>

DISCLAIMER:  The mention of "death squads" was not meant to discredit
Atari, Corp in any way.  Also, I do not want to trivialize the grim
reality of death sqauds in some parts of "civilization".

braner@batcomputer.TN.CORNELL.EDU (braner) (10/20/86)

[]

Reminder: after running this program ONCE, holding down
Control, Alternate and BOTH Shift keys and pressing any
other key will stop the program running at the time, as long
as it is in user mode.  Since programs call OS routines
which execute in supervisor mode, and since this program will
act only if the interrupted program is in user mode, you may
have to repeat pressing the last key several times.

The two bombs were due to the RTE to $FC0A5A in user mode,
where a MOVE.L #$93A,$4A2 is attempted.  Here is a corrected
version with BSET #5,(A7) added before the RTE, to keep it
in supervisor mode.

(Hex dump of the complete program - 96 bytes):
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
60 1A 00 00 00 70 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 FF FF 48 7A 00 18
3F 3C 00 26 4E 4E 5C 8F 42 67 2F 3C 00 00 02 00
3F 3C 00 31 4E 41 48 7A 00 08 21 DF 01 18 4E 75
08 17 00 05 66 14 0C 38 00 0F 0E 1B 65 0C 08 D7
00 05 2F 7C 00 FC 0A 5A 00 02 4E F9 00 FC 28 1C
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Have a clean break!

- Moshe Braner

<braner@amvax.tn.cornell.edu>