* QL WORLD DIY TOOLKIT - TASK COMMANDER ROUTINES
* Version 0.8, Copyright July 1991 Simon N Goodwin
*
* BP.INIT expects a PROC name & address to be inserted at PARAMS
*
setup      lea.l    params,a1       Point at the details
           move.w   $110\w,a2       Fetch BP.INIT vector
           jmp      (a2)            Add a resident procedure
params
*
not_open   moveq    #-6,d0          CHANNEL NOT OPEN error code
bad_return rts
*
* TASK_CMD_NAME { # CHANNEL% , }   [ PARAMETER$ ]   [ , ]
* Trailing comma at the end signifies EXEC with no need to wait
*
taskcmd    moveq    #0,d7           Default channel count
           moveq    #0,d5           Default parameter string: ""
           cmpa.l   a3,a5           Check parameter pointers 
           beq.s    make_task
           btst     #4,-7(a5,a6.l)  Is there a trailing comma?
           beq.s    checked
           bset     #31,d5          Aha, it's EXEC not EXEC_W
           tst.b    -8(a5,a6.l)     Is there a data type too?
           bne.s    checked
           subq.l   #8,a5           Forget the last 'parameter' ,
           cmpa.l   a3,a5           Is there anything else?
           beq.s    make_task
checked    tst.b    -7(a5,a6.l)     Does the last one have a # ?
           bmi.s    get_chans       Positive if not, assume $
*
* Pick the string parameter off the end of the parameter list
*
get_string movea.l  a3,a4           Note location of parameters
           lea.l    -8(a5),a3       Focus on the last one
           movea.w  $116\w,a2       Pick up CA.GTSTR vector
           jsr      (a2)            Get string onto RI stack
           bne.s    bad_return
           move.w   0(a1,a6.l),d5   D5 is text length, 0-32767
           bne.s    make_task       
           addq.l   #2,$58(a6)      Adjust BV.RIP; null length
           cmpa.l   a3,a4           Is there anything else?
           beq.s    make_task
           movea.l  a3,a5           Find the other parameters
           movea.l  a4,a3           Recall pointer to first
get_chans  movea.w  $118\w,a2       Fetch CA.GTLIN vector
           jsr      (a2)            Stack some long integers
           bne.s    bad_return
           move.w   d3,d7           Update channel count
*
* Check each channel number before allocating task memory
*
           movea.l  a1,a4           Keep RI stack base offset
check_chan move.w   2(a1,a6.l),d0   Get LOW word of parameter 1
           mulu     #40,d0          Each table entry is 40 bytes
           add.l    48(a6),d0       Add channel base offset
           cmp.l    52(a6),d0       Check it is within the table
           bge.s    not_open
           move.l   0(a6,d0.l),d0   Pick up the Qdos channel ID
           bmi.s    not_open
           move.l   d0,0(a1,a6.l)   Store ID in place of #number
           addq.l   #4,a1           Advance to next parameter
           subq.w   #1,d3           Process all in turn
           bne.s    check_chan
           movea.l  a4,a1           Restore A1
*
* D7.W is No. of channels; D5.W is text length; D5.L<0 for EXEC
* Parameter values are in order on the RI stack; now make a task
*
make_task  move.l   #0,d3           Data space - to be patched
           move.l   #2,d2           Code space - to be patched
           moveq    #-1,d1          Owner is this task
           suba.l   a1,a1           A1 is 0; put task in TRNSP
           moveq    #1,d0           MT.CJOB trap key
           trap     #1              Create a task
           tst.l    d0              Did that work?
           bne.s    return_err
           lea.l    8(a0,d2.l),a4   A4 -> start of data space +8
           lea.l    -8(a4,d3.l),a3  A3 points past end of dspace
*
* Copy the code into the task area; ignore slight over-runs
*
           lsr.l    #4,d2           Divide by 16 (roughly)
           move.l   a0,a1           Copy pointer to code space
           lea.l    task_code,a2    Find the code
copy_code  move.l   (a2)+,(a1)+     Repetition improves speed
           move.l   (a2)+,(a1)+
           move.l   (a2)+,(a1)+     Move 16 bytes per DBRA step
           move.l   (a2)+,(a1)+
           dbra     d2,copy_code    Copy up to 1 Megabyte, fast
*
* Copy the parameters from RI to JB.SP, counting 16 bit words
*
           move.w   d7,d0
           add.w    d0,d0           Number of channel ID words
           tst.w    d5              Check string length
           bne.s    string
no_string  clr.w    -(a3)           Stack a null length           
           bra.s    adjust_a3
string     addq.w   #3,d5           Include length + an odd byte
           lsr.w    #1,d5           D5 := string size in words
           add.w    d5,d0           D0 := stack words required
adjust_a3  sub.w    d0,a3           Move A3 down to make room
           sub.w    d0,a3           D0 is in words, A3 is bytes
           cmpa.l   a4,a3           Ensure room for parameters
           bcs.s    activator       Pass nowt if they won't fit
           movea.l  a3,a4           Save bottom address
           move.l   $58(a6),a1      Recall the BV.RIP offset
           bra.s    copy_loop       Copy nothing if D0=0
copy_pars  move.w   0(a1,a6.l),(a3)+
           addq.l   #2,a1           Advance up the RI stack   
copy_loop  dbra     d0,copy_pars 
*
* If CHANS=0, stack 1.W and the ID from #1, else stack CHANS.W
*
chan_check tst.w    d7              Check channel count
           bne.s    stack_ok
           moveq    #1,d7           Pass default, ID of #1
           movea.l  48(a6),a2       A2 is offset to BV.CHBAS
           move.l   40(a6,a2.l),-(a4)  Presume that #1 exists
stack_ok   move.w   d7,-(a4)        Stack the channel count
           move.l   a4,-12(a0)      Set JB.A7 in task header
*
* Activate the task
*
activator  moveq    #16,d2          Default priority (0-255) 
           moveq    #-1,d3          Timeouts 0 = EXEC, -1 = WAIT
           tst.l    d5              Did command end with a comma?
           bpl.s    time_set        If not, timeout is -1, wait
           moveq    #0,d3           EXEC; no need to wait
time_set   moveq    #10,d0          MT.ACTIV trap key
           trap     #1
return_err rts                      Return error code (if any)
*
task_code  end                      Task code follows here

