;
; Break.asm
;
; Written: Jan/7/1987 by SDB
;
;       This program may be freely distributed.  Improvements are welcome,
;       but any code based on this program can never become proprietary.
;       You are welcome to use, improve, and distribute this program.
;       You are forbidden to prevent anyone else from using, improving,
;       or distributing this program.
;
;       This is a contribution to Cheath's Abolish ADOS Commands series.
;       Let's get them all done, and boogie!
;
;       I have only assembled this with the old Manx assembler, it comes
;       in at 488 bytes.  The Metacompost assembler should shave about 72
;       bytes or so off of that. (The BCPL Break is 980 bytes in size).
;
;       Syntax is the same as the old break, except you don't need to
;       put spaces between the flags:
;
;       Break 4 c d e f
;       Break 4 cdef
;
;       work the same.
;
; Register usage:
;       a6 - Current Lib base we need (exec or dos)
;       a5 - storage for DOSBase
;       a2 - pointer to task array
;       a0 - pointer to next character of command line.
;       d2 - task number to signal
;       d3 - signals to send to task.
;       a2 - pointer to task array
;
;       all others, pretty much scratch as scratch can.
;
_LVOOpenLibrary         equ     -552
_LVOCloseLibrary        equ     -414
_LVOForbid              equ     -132
_LVOPermit              equ     -138
_LVOSignal              equ     -324

_AbsExecBase            equ     4

_LVOOutput              equ     -60
_LVOWrite               equ     -48

dl_Root                 equ     34
TASK_SIZE               equ     92
LIBRARY_VERSION         equ     31      ; Don't need anything higher.

uselen                  equ     49      ; length of How To Use message
tasklen                 equ     26      ; length of You Screwed Up message

;
SIGBREAKF_CNTRL_C       equ     $1000   ; Default signal == ^C
SIGBREAKF_ALL           equ     $f000   ; Send all signals.
;
;
;       Need the following entry and rts for manx assembler/linker.
;
        entry   Start
        rts
Start:
        move.l  a0,-(sp)
        move.l  _AbsExecBase,a6
        lea     DOSName(pc),a1
        moveq.l #LIBRARY_VERSION,d0
        jsr     _LVOOpenLibrary(a6)
        tst.l   d0
        bne.s   good
        addq.w  #4,sp                   ; Things are bad.  No dos.
        move.l  #20,d0
        rts
good:   move.l  d0,a5                   ; keep dos pointer here for now
        moveq.l #0,d2                   ; zero target_task
        move.l  d2,d3                   ; zero target_signals
        move.l  (sp)+,a0
lex:
        bsr.s   getnxt          ; get upper case char in d0
        cmp.b   #'F',d0         ; characters greater than F are never valid
        bhi.s   usage           ; so gently explain how to use.
        cmp.b   #' ',d0
        beq.s   lex             ; skip blanks
        cmp.b   #$0a,d0         ; check for eol
        bne.s   cntrlp
        tst.l   d2              ; Check args - if d2 is still zero, then
        beq.s   usage           ; Send'em a how to message.
        bra.s   doit
getnxt:                         ; return an upper case character
        moveq.l #0,d0           ; clear
        move.b  (a0)+,d0
        cmp.b   #'a',d0
        bcs.s   getrts
        and.b   #$5f,d0
getrts: rts

cntrlp: 
        cmp.b   #'A',d0         ; if one of C D E or F, calculate the
        bls.s   allp            ; value of the signal to send the tas k.
        sub.b   #$37,d0         ; get the bit number
        moveq.l #1,d1
        asl.l   d0,d1           ; get the mask
        or.l    d1,d3           ; add to current signals
        bra.s   lex
allp:
        cmp.b   #'A',d0         ; All flags?
        bne.s   digitp
        bsr.s   getnxt
        cmp.b   #'L',d0
        bne.s   usage
        bsr.s   getnxt
        cmp.b   #'L',d0
        bne.s   usage
found:
        move.l  #SIGBREAKF_ALL,d3       ; set all flags
        bra     lex
digitp:                         ; must be a numeric...
        tst.l   d2              ; but only one per customer
        bne.s   usage           ; or syntax error (can only signal one task)
dodigit:
        sub.b   #'0',d0
        blt.s   usage
        cmp.b   #9,d0
        bgt.s   usage
        add.l   d2,d2
        move.l  d2,d1
        asl.l   #2,d1
        add.l   d1,d2
        add.l   d0,d2
        bsr.s   getnxt
        cmp.b   #' ',d0
        beq.s   lex
        cmp.b   #$0a,d0
        bne.s   dodigit
doit:
        tst.l   d3                      ; check target signals
        bne.s   sigsok
        move.l  #SIGBREAKF_CNTRL_C,d3   ; If no flags, set default.

sigsok: move.l  _AbsExecBase,a6
        jsr     _LVOForbid(a6)
        move.l  dl_Root(a5),a1
        move.l  (a1),a2         ; BPTR to task array
        move.l  a2,d0
        asl.l   #2,d0
        move.l  d0,a2           ; pointer to task array -- check for errors
        cmp.l   (a2),d2         ; Task# can't be outside of taskarray bounds
        bge.s   task_error
        tst.l   d2
        beq.s   task_error      ; task can't be zero. (Have to check here
        asl.l   #2,d2           ; as well as in lex routines.)
        move.l  0(a2,d2.l),d0   ; or non-existant
        beq.s   task_error
        ;
        ; task now holds pointer to msg port of task
        ; subtract #bytes in a tcb to get the actual address of the task
        ; for signal.
        ;
        move.l  d0,a1
        sub.l   #TASK_SIZE,a1
        ;
        ; now send the signals to our task.
        ;
        move.l  d3,d0
        jsr     _LVOSignal(a6)
        jsr     _LVOPermit(a6)
        clr.l   -(sp)
        bra.s   bye             ; exit(0)
;
; Error Handlers
;
; Usage - just tell folks how to use, and beat it.
;
usage:
        lea     usemsg(pc),a0
        move.l  #uselen,d3
        moveq   #0,d0
        bra.s   write_it
;
; task_error - Call this when user tries to signal a non-existant task.
;
task_error:
        jsr     _LVOPermit(a6)  ; turn multitasking back on
        lea     taskmsg(pc),a0
        move.l  #tasklen,d3
        move.l  #20,d0
;
; Write the message. Enter with length in d3, strptr in a0, and exit code
; in d0.
;
write_it:
        move.l  d0,-(sp)                ; save exit code
        move.l  a0,d2                   ; put strptr where write expects it
        move.l  a5,a6                   ; get DOSBase in a6
        jsr     _LVOOutput(a6)
        move.l  d0,d1
        jsr     _LVOWrite(a6)
bye:                                    ; common exit for all routines
        move.l  a6,a1                   ; except if dos doesn't open
        move.l  _AbsExecBase,a6
        jsr     _LVOCloseLibrary(a6)
        move.l  (sp)+,d0
        rts
;
; data area
;

usemsg:         dc.b    "Usage: Break <task number> [ALL] [C] [D] [E] [F]",10
taskmsg:        dc.b    "That task does not exist.",10
DOSName:        dc.b    "dos.library",0

