koreth@ssyx.ucsc.edu (Steven Grimm) (08/25/88)
Submitted-by: uunet!mcvax!philmds!leo (Leo de Wit)
Posting-number: Volume 1, Issue 71
Archive-name: break
The following program, when placed in the AUTO folder or started by hand,
lets you interrupt any program (except the desktop).
The corresponding binary is sent to the moderator of comp.binaries.atari.st.
The program was assembled and linked with the assembler and linker from the
GST-C compiler.
For correspondence conceirning this program (bugs, questions etc.) try
L. J. M. de Wit
Nachtegaallaan 7
5731XP Mierlo
Holland
e-mail: ..!mcvax!philmds!leo
(or perhaps ..!hp4nl!philmds!leo)
------------------------------ starts here -----------------------------------
******************************************************************************
* *
* break.asm version 1.0 of 20 August 1988 (C) L.J.M. de Wit 1988 *
* *
* This software may be used and distributed freely if not used commercially *
* and the originator (me) is mentioned. *
* *
******************************************************************************
*
* NAME
* break - stop current program on receipt of interrupt character
*
* SYNTAX
* break [-e|-d|-z] [-i|-c<code>]
*
* DESCRIPTION
*
* After installing break, any program can be interrupted.
* This is achieved by 'extending' the existing keyboard interrupt
* routine: after executing the old code the break character check
* is done.
*
* The various flags have the following meaning:
* e(nable) : the current break character will end the program.
* the code returned is -32, what seems to be the standard
* value for programs interrupted by ^C in GEMDOS.
* d(isable): no actions are done; this restores the old behaviour
* z(ero) : the current break character will be discarded (made 0)
* in the input buffer; this can be used to disable ^C.
* i(nput) : the break character is prompted for. Combinations with
* shift, control and alternate keys are also allowed.
* Useful for specifying the break character interactively.
* c(ode) : specifies the break character as a hexadecimal code.
* The hex code must follow the 'c' flag immediately.
* Useful for specifying the break character from a script.
* Of the flags e,d and z only one should be used; e is the default.
* Also, of the flags i and c only one should be used; control-delete is
* the default. This is done on purpose; you can always change it to ^C
* if you want to (or whatever key you like).
*
* The break program can be reused indefinitely, because a next invocation
* is not made memory resident; it only modifies parameters in the first
* invocation (the resident one).
*
* The program can be placed into the \AUTO folder to be installed
* automatically, or activated 'by hand'. If placed in the \AUTO folder, it
* should of course have a .PRG extension (break.prg); as \AUTO folder
* programs don't get arguments, the break will be enabled and the break
* character is control-delete in this case.
*
* BUGS/SHORTCOMINGS
* A nice extension would be the possibility to catch the interrupt from
* a user program; this could be achieved by using a new trap. As this
* implies restoring the old interrupt catch routine when the program
* exits, and maybe also core dumps could be added to the action of an
* (other) interrupt character, such a more general signal mechanism is
* not added (yet). Gives me time to think of a nice implementation 8-).
*
* JOKE
* Gimme a break, huh?!
*
module break
section s.ccode
* character codes
tab equ 9
lf equ 10
cr equ 13
* GEMDOS & (X)BIOS stuff
gemdos equ 1
bios equ 13
xbios equ 14
ptermres equ $31
setexc equ 5
cconws equ 9
crawcin equ 7
super equ $20
pterm0 equ 0
pterm equ $4c
iorec equ 14
* divers
bpaglen equ $100
textlen equ 12
datalen equ 20
bsslen equ 28
intrupt equ -32 * Code returned by interrupted programs
curproc equ $602c * Contains base page start of current process
* iorec struct offsets; the buffer ptr. itself has offset 0
ibufsize equ 4
ibufhead equ 6
ibuftl equ 8
ibuflow equ 10
ibufhi equ 12
brkinit
move.l 4(sp),a0
adda.w #$80,a0 * a0 points to argument string start
moveq.l #0,d0
move.b (a0)+,d0 * Get string length &
clr.b (a0,d0.w) * ensure '\0' termination
nxtbyt
move.b (a0)+,d0
beq nxtdone * '\0' char is end of string
cmp.b #'A',d0
blt.s arge
cmp.b #'Z',d0
bgt.s arge
or.b #$20,d0 * Convert uppercase to lower
arge
cmp.b #'e',d0 * enable
bne.s argd
st enabled * enabled = TRUE
bra.s nxtbyt
argd
cmp.b #'d',d0 * disable
bne.s argi
sf enabled * enabled = FALSE
bra.s nxtbyt
argi
cmp.b #'i',d0 * input
bne.s argc
move.l a0,-(sp)
pea.l inpmsg
move.w #cconws,-(sp)
trap #gemdos * Prompt for break char
addq.l #6,sp
move.w #crawcin,-(sp)
trap #gemdos * Read the char
addq.l #2,sp
move.l d0,breakcode * and store it
pea.l retmsg
move.w #cconws,-(sp)
trap #gemdos * Print a cr/lf
addq.l #6,sp
move.l (sp)+,a0
bra.s nxtbyt
argc
cmp.b #'c',d0 * code of break char in hex
bne.s argz
moveq.l #0,d1 * d1 will contain the code
argcnxt
move.b (a0)+,d0
bne.s argcnz * Not end of arg string yet
move.l d1,breakcode * If end, store break char
bra nxtdone * and goto end of arg string interpretation
argcnz
cmp.b #' ',d0 * End of number found
beq.s argcend
cmp.b #tab,d0 * this one too
beq.s argcend
cmp.b #'0',d0 * Now follows a series of tests and
blt.s userr * conversions to find the hex digit's value.
cmp.b #'a',d0
blt.s argcnlc
sub.w #32,d0 * convert lower case to upper
argcnlc
cmp.b #'F',d0
bgt.s userr
cmp.b #'9',d0
ble.s argcnum
cmp.b #'A',d0
blt.s userr
subq.l #7,d0 * Make up for diff between '9' and 'A'
argcnum
sub.b #'0',d0 * '0' is the base: 0
asl.l #4,d1 * Multiply by 16
or.b d0,d1 * OR in the 4 low digits of d0 into d1
bra.s argcnxt
argcend
move.l d1,breakcode * Store the break key code
bra nxtbyt
argz
cmp.b #'z',d0
bne.s argsep
st zeroed * Set the 'zeroed' flag
bra nxtbyt
argsep
cmp.b #' ',d0 * Accept space
beq nxtbyt
cmp.b #tab,d0 * and tab
beq nxtbyt
cmp.b #'-',d0 * and hyphen as separators (not strictly)
beq nxtbyt
userr
pea.l usemsg * If error in arg string show usage message
move.w #cconws,-(sp)
trap #gemdos
addq.l #6,sp
move.w #1,-(sp)
move.w #pterm,-(sp)
trap #gemdos * and terminate with error status
nxtdone
move.w #1,-(sp)
move.w #iorec,-(sp)
trap #xbios
addq.l #4,sp
move.l d0,iop * Save pointer to iorec struct
move.l #-1,-(sp)
move.w #$46,-(sp)
move.w #setexc,-(sp)
trap #bios
addq.l #8,sp
move.l d0,oldvec * Save old keyboard interrupt vector
lea.l breakey,a0
sub.l a0,d0
move.l d0,diff * Difference between start addr of old and new
clr.l -(sp)
move.w #super,-(sp)
trap #gemdos * Supervisor mode
addq.l #2,sp
move.l d0,(sp) * Save supervisor stack pointer on stack
lea.l magic,a0 * a0 points to 'magic' string in this prog
move.l diff,d0
lea.l (a0,d0.l),a1 * a1 points (perhaps) to 'magic' in old
move.l #(magicend-magic-1),d0 * # chars in 'magic' string minus one
chkmag
cmp.b (a0)+,(a1)+ * Check strings for equality
dbne d0,chkmag
beq.s mageq * If old prog DID contain magic string; else:
magne
move.w #super,-(sp)
trap #gemdos * Back to user mode (sp was still on stack)
addq.l #6,sp
clr.l diff * First incarnation: will be made resident
pea.l breakey * breakey will be new keyboard int. vector
move.w #$46,-(sp)
move.w #setexc,-(sp)
trap #bios * Set it
addq.l #8,sp
move.l 4(sp),a0 * basepage start
move.l #bpaglen,d0 * base page length
add.l textlen(a0),d0 * + text length
add.l datalen(a0),d0 * + data length
add.l bsslen(a0),d0 * + bss length
clr.w -(sp) * return value: 0 for success
move.l d0,-(sp) * # bytes to keep
move.w #ptermres,-(sp) * keep process
trap #gemdos * stops here...
mageq
move.w #super,-(sp) * A next incarnation of break in this branch.
trap #gemdos * Back to user mode (sp was still on stack)
addq.l #6,sp
move.l diff,d0
lea.l enabled,a0
move.b (a0),(a0,d0.l) * Copy 'enabled' into old image
lea.l zeroed,a0
move.b (a0),(a0,d0.l) * Do this too for 'zeroed'
lea.l breakcode,a0
move.l (a0),(a0,d0.l) * And also for the current break char
move.w #pterm0,-(sp)
trap #gemdos * Exits here with 0 as status.
breakey
* The new interrupt routine
pea.l breaketc * Return to breaketc after executing old code
move.w sr,-(sp) * This is because old code ends with 'rte'
move.l oldvec,-(sp) * Push old vector onto stack
rts * and jump to it
breaketc
movem.l d0-d1/a0-a2,-(sp)
tst.b enabled
beq.s breakend * If not enabled do nothing
move.l iop,a0
move.w ibuftl(a0),d0
cmp.w ibufhead(a0),d0
beq.s breakend * If empty keyboard buffer do nothing
moveq.l #2,d0
move.l curproc,a1
testbp
tst.l (a1) * At end of list?
beq.s breakend * Cur. prog was desktop or so; don't break
move.l 36(a1),a1 * Back link to previous process
dbra d0,testbp
moveq.l #0,d0
move.w ibufhead(a0),d0 * d0 is used as index into char buffer
move.l breakcode,d1
move.l (a0),a1 * a1 points to character buffer
testbuf
cmp.w ibuftl(a0),d0
beq.s breakend * Checked them all
addq.l #4,d0 * Increment index in buffer
cmp.w ibufsize(a0),d0 * If at buffer size
bne.s notatend
moveq.l #0,d0 * wrap it (circular buffer)
notatend
cmp.l (a1,d0.w),d1 * Is char at this index the break char?
bne.s testbuf * No; check next
tst.b zeroed * Yes; is 'zeroed' flag set?
beq.s notzeroed
clr.l (a1,d0.w) * Make the break char in the buffer 0;
bra.s testbuf * this eliminates any special meaning
notzeroed
move.w ibuftl(a0),ibufhead(a0) * Clear the entire buffer
move.l #stop,22(sp) * Return will be to the routine 'stop'
breakend
movem.l (sp)+,d0-d1/a0-a2
rte
stop
move.w #intrupt,-(sp) * Code for interrupted programs
move.w #pterm,-(sp)
trap #gemdos * and stop here
section s.data
iop dc.l 0 * Pointer to iorec struct
breakcode dc.l $0053001f * Default is control_delete
diff dc.l 0 * Difference in byte distance old <-> new
oldvec dc.l 0 * previous val of interrupt vector
enabled dc.b $ff * 'enabled' is default true.
zeroed dc.b 0 * 'zeroed' is default false.
magic dc.b 'BREAK MARKER',0 * magic string that identifies this prog.
magicend
inpmsg dc.b 'Input the break code by hitting the key(s) : ',0
usemsg dc.b 'break: usage: break [-e|-d|-z] [-i|-c<code>]',cr,lf,0
retmsg dc.b cr,lf,0
end