[comp.sources.atari.st] v01i072: newbreak -- New version of "break" program

koreth@ssyx.ucsc.edu (Steven Grimm) (09/01/88)

Submitted-by: ames.arc.nasa.gov!atari!apratt (Allan Pratt)
Posting-number: Volume 1, Issue 72
Archive-name: newbreak

[This is a new version of Leo de Wit's "break" program, recently posted.
 It avoids the use of undocumented locations, so should be compatible with
 all versions of TOS. -sg]

Here is the source to your break.s, as modified by me.  There is no
command line option to change the default hard-break key -- I'm just
lazy, I guess.

******************************************************************************
*                                                                            *
*    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.                                      *
*                                                                            *
******************************************************************************

* 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

* 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		; dist between start of old and new
      clr.l    -(sp)
      move.w   #super,-(sp)
      trap     #gemdos		; Supervisor mode
      addq.l   #2,sp
      move.l   d0,(sp)
      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 - 1
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)	; second run of this program
      trap     #gemdos		; Back to user mode
      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:
      tst.b    enabled
      beq      dorte		; If not enabled do nothing

      movem.l  d0-d1/a0-a2,-(sp)
      move.l   iop,a0
      move.w   ibuftl(a0),d0
      cmp.w    ibufhead(a0),d0
      beq.s    dontbreak	; If empty keyboard buffer do nothing
      move.l   (a0),a1
      move.l   (a1,d0.w),d1	; get the last entry
      and.l    #$00ff00ff,d1	; mask only scan & ASCII part
      cmp.l    breakcode,d1	; check against break code
      beq.s    normalbrk
      cmp.l    hardbreak,d1	; check against stronger break code
      bne.s    dontbreak
      moveq.l  #-1,d1		; make long negative - means hard break
normalbrk:
      tst.b    zeroed		; we have a break; is 'zeroed' flag set?
      bne.s    dozeroed
      subq.w   #4,d0		; DECREMENT d0 with wrap
      bpl.s    wrapdone
      add.w    ibufsize(a0),d0
wrapdone:
      move.w   d0,ibuftl(a0)
      bra.s    gotbreak
dozeroed:
      move.w   ibuftl(a0),ibufhead(a0)	    ; Clear the entire buffer
gotbreak:
      tst.l    d1		; hard break?
      bmi.s    dobreak
      btst.b   #5,20(sp)	; test Super bit on stacked SR
      bne.s    dontbreak

dobreak:
      movem.l  (sp)+,d0-d1/a0-a2
      move.l   #stop,2(sp)	; Return will be to the routine 'stop'
      rte

dontbreak:
      movem.l  (sp)+,d0-d1/a0-a2
dorte:
      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  $002E0000		; Default is alt-C
hardbreak:  dc.l  $002D0000		; Alt-X means I really mean it!
diff:       dc.l  0			; Difference in 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 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



============================================
Opinions expressed above do not necessarily	-- Allan Pratt, Atari Corp.
reflect those of Atari Corp. or anyone else.	  ...ames!atari!apratt