***********************************************************************
* structure of signal handler : simple case
*  $00     word        $4afc
*  $02     long        '%SIG'      magic
*  $06     long        sighnd      pointer to handler routine or NULL

*  $0a     long        stackmin    no interrupt if sp < stackmin                   
*  $0e     long        priority    
***
* and the coresponding defines

h_magic    equ         0
h_type     equ         2
*

hs_handler equ         6
hs_stackmin equ        $a
hs_prio    equ         $e

*********************
* structure of signal handler : extended handler
*  $00     word        $4afc                   
*  $02     long        '%CSG'          magic
*  $06     long        MAXSIG          number of signals handled
*          long        *sigcurrent[MAXSIG+1]  ptr to array of handlers
*          long        *sigdefault[MAXSIG+1]  ..default.. or 0
*          long        *sa_mask[MAXSIG+1]       sigs 2be blckd on enctr o'that1
*          word        hprot_id
**                                 0 : single value
**                                 1 : *level[MAXSIZ+1]
*          long        (*)hprotlevel([])
*          word        stack_id
**                                 0 : lowstacklimit
**                                 1 : *{sttop;stbottom}   ; 1 xtra stack
**                                 2 : *{sttop;stbottom}[MAXSIZ+1]
*          long        (*)stackstruct([])       lowstacklimit or extra stacks
*          word        pri_id
**                                 0 : priority
**                                 1 : *priority[MAXSIZ+1]
*          long        (*)pristruct([])         priority(ies)
*          long        *siginfo    
*          long        (*sigvec)()
***
*** some status info that is easier to handle here
*          word        cprotlevel  jobs current protection level
*
*          byte        deffer      intern flag, deferred signal
*          byte        ixx         intern flag, checksig in progress
*
*          word        activ       set when signalhandling, cleared by signal(),
*                                  sigaction(), sigreturn()
*                                  to avoid handler beeing reset to old
*          long        uval        NULL or *uval[nsigs+1], stored# 
*          long        block                    block this signals
*          long        pending                  mask of # signals
*          long        resethandler             # if handling 1of these ..
*** 
* and its defines

hc_nsigs   equ         6           .l
hc_curr    equ         10          .l
hc_def     equ         14          .l
hc_samask  equ         18          .l
hc_protid  equ         22          .w
hc_protlev equ         24          .l
hc_stackid equ         28          .w
hc_stack   equ         30          .l
hc_priid   equ         34          .w
hc_prio    equ         36          .l
hc_siginf  equ         40          .l
hc_sigvec  equ         44          .l
hc_chlev   equ         48          .w
hc_defer   equ         50          .b
hc_ixx     equ         51          .b
hc_activ   equ         52          .w
hc_uval    equ         54          .l
hc_block   equ         58          .l
hc_pend    equ         62          .l
hc_reset   equ         66          .l
** total 70 **


* messages for intern and global communication
ml.nxt     equ         4           ; message link block
ml         equ         8          ;   ... size 
* message header definition
msg.magic  equ         0           ; '%MSG'
msg.len    equ         4
msg.type   equ         8           ; .w  0 signal, 1 timer event
msg.txi    equ         10          ; .w  timer info (unit),0=ticks,1=secs

msg.jobid  equ         12          ; .l
msg.signr  equ         16          ; .l
msg.prio   equ         20          ; .l
msg.uval   equ         24          ; .l
*
sigmsg_len equ         28

msg.t_evid equ         28          ; .l  timer event id, 0 reserved, 1=alarm, 
msg.t_ticks equ        32          ; .l  send signal after ticks units
msg.t_int  equ         36          ; .l  interval timer if >0
tmsg_len   equ         40

msg_maxlen equ         tmsg_len
msg_minlen equ         sigmsg_len
*
TIMER      equ         1           ; message type
ALARM      equ         0


SIG_IGN    equ         0
SIG_DFL    equ         1

* priority:
WFJOB      equ         %111000000000  ; mask interrupt if waiting for job
SUSP       equ         %000000111000  ;                              trap#1
WFIO       equ         %000111000000  ;                              trap#3
NORM       equ         %000000000111  ;        normal execution

* optional flags for:
*   priority_s:
DF_WIO     equ         %10000000000000  ; defer if waitiong for io
DF_SUSP    equ         %1000000000000   ;          suspended
DF_WJOB    equ         %100000000000000 ;          waiting for job
*SIGPG     equ         %1000000000000000 ; send to process group

*   priority_r:
*PGROOT    equ        %1000000000000   ; root of process group

* priority: mask of reserved bits
P_RES_R    equ    ^(NORM+SUSP+WFIO+WFJOB)      
P_RES_S    equ    ^(DF_WIO+DF_SUSP+DF_WJOB+NORM+SUSP+WFIO+WFJOB) 

***********
* block of saved registers & jobs state (struct SIGSVB )
* used for %SIG handler
svb.finish equ         $0          ; pointer to interrupt cleanup routine,
*                                    usualy called by rts
svb.sgn    equ         $4          ; .W : 0 normal, 1 io-susp, 2 waitforjob
*                                  *      3 susp
sgn_nrm    equ         0           ; normal
sgn_ios    equ         1           ; io suspension
sgn_wfj    equ         2           ; wait for job
sgn_sus    equ         3           ; suspension
sgn_raise  equ         4           ; raise() ed signal
* rest of structure is undefined for raised signals


svb.fs1    equ         $6          ; 2 svb.sgn specific longwords
svb.fs2    equ         svb.fs1+4

svb.d4     equ         svb.fs2+4   ; saved registers
svb.d5     equ         svb.d4+4
svb.d6     equ         svb.d5+4
svb.a4     equ         svb.d6+4
svb.a5     equ         svb.a4+4
svb.a7     equ         svb.a5+4

svb.sr     equ         svb.a7+4
svb.ret    equ         svb.sr+2

svb_len    equ         svb.ret+4


*** extended handler sigcontext ***
* struct sigcontext / esvb used for %CSG handlers
* 
* svb.*                                        ; same as above
esvb.oblock equ        svb_len                 ; block mask to restore
esvb.ohand equ         esvb.oblock+4           ; handler to restore or 0
esvb.signr equ         esvb.ohand+4
esvb.hstruct equ       esvb.signr+4            ; pointer to handler struct
esvb.uval  equ         esvb.hstruct+4          
esvb.d0    equ         esvb.uval+4 ; saved compiler scratch registers
esvb.d1    equ         esvb.d0+4
esvb.d2    equ         esvb.d1+4
esvb.a0    equ         esvb.d2+4
esvb.a1    equ         esvb.a0+4
esvb_len   equ         esvb.a1+4

* shadow definitions for cint_ret hack
esvb.ret   equ         esvb_len-4
esvb.sr    equ         esvb.ret-2

***********************************

* temporary storage block for cseval and do_int
tmp.htype  equ         0                       ; type of handler
tmp.haddr  equ         tmp.htype+4 ; addr of handler routine
tmp.stack  equ         tmp.haddr+4 ; stackbottom
tmp.prio   equ         tmp.stack+4
tmp.force  equ         tmp.prio+4              ; flag, ignore blocking
tmp.sregs  equ         tmp.force+2

tmp_size   equ         tmp.sregs+3*4


* QDOS system definitions

TRAP3      equ         $4e43       ; opcode
SUPER      equ         $2000       ; s-bit in sr

SIGKILL    equ         9           ; defined in signal.h
SIGTERM    equ         15
NOBLOCKMASK equ        ^(1<<(SIGKILL-1))       ; can't block this
NOCATCH    equ         SIGKILL

EINTR      equ         -1          ; frjob return code

sgn_def    equ         100         ; intern define

*   Trap #1 keys

sms.info   equ         $00         ; get system INFOrmation
sms.achp   equ         $18         ; Allocate Commaon HeaP
sms.lpol   equ         $1c         ; Link in POLled action
sms.lshd   equ         $1e         ; Link in SCheDuler action
sms.liod   equ         $20         ; Link in IO Device driver

*   Vectors

mm.alchp   equ         $c0
mm.rechp   equ         $c2
ut.wtext   equ         $d0         ; Write TEXT vector


*   System Variables

sv_jbpnt   equ         $64         ; current JoB PoinTer
sv_jbbas   equ         $68         ; JoB Table Base
sv_jbtop   equ         $6c         ; JoB Table Top
sv_chmax   equ         $72
sv_chbas   equ         $78         ; CHannel Table Base
sv_chtop   equ         $7c         ; CHannel Table Top


*   Jobheader Offsets

job.stat   equ         $14
job.tag    equ         $10
job.wflag  equ         $17
job.wjob   equ         $18
job.d0     equ         $20
job.d1     equ         job.d0+4
job.d2     equ         job.d1+4
job.d3     equ         job.d2+4
job.d4     equ         job.d3+4
job.d5     equ         job.d4+4
job.d6     equ         job.d5+4

job.a0     equ         $40
job.a1     equ         job.a0+4
job.a2     equ         job.a1+4
job.a3     equ         job.a2+4
job.a4     equ         job.a3+4
job.a5     equ         job.a4+4
job.a6     equ         job.a5+4

job.pc     equ         $62
job.sr     equ         $60
job.sp     equ         $40+7*4
job.a7     equ         job.sp


*   Signal device offsets

dev_plblk  equ         $8                      ; poll link block
dev_pladr  equ         $c
dev_scblk  equ         $10                     ; sched link block
dev_scadr  equ         $14

dev_link   equ         $18
dev_io     equ         $1c         
dev_open   equ         $20         
dev_close  equ         $24         
dev_joblist equ        dev_close+4           ; ll of signal-recving channels
dev_nxevent equ        dev_joblist+4           ; ptr to next timer event
dev_nticks equ         dev_nxevent+4           ; #ticks till next event
dev_gticks equ         dev_nticks+4            ; global tick count
dev_scratch equ        dev_gticks+4

dev_size   equ         dev_scratch+48

* trap #3 interface
SEND.SIG   equ         $2f
SEND.MSG   equ         $7
SR.MSG     equ         $4
INFO       equ         $3

UCMASK0    equ         $ffdfdfdf
UCMASK1    equ         $dfdfdfff


*  Channel block definition

ch_driver  equ         $4
ch_jobwt   equ         $14
ch_actn    equ         $13
ch_tag     equ         $10
ch_stat    equ         $12
ch_ojob    equ         $08

*  private channel variables

ch_nxtch   equ         $14+4                   ; private channel variables
ch_sigh    equ         ch_nxtch+4              ; pointer to handler struct
ch_events  equ         ch_sigh+4               ; ll of timer events

ch_len     equ         ch_events+4

ERR.NC     equ         -1
ERR.NJ     equ         -2
ERR.OM     equ         -3
ERR.OR     equ         -4
ERR.NO     equ         -6
ERR.BP     equ         -15
ERR.NI     equ         -19
ERR.BL     equ         -21
  

         SECTION     TEXT

***********************************************************
* dev init code

install:
*   Check that we are not running on an SMS2 system.
*   This is because SMS2 has a different layout for
*   the job header than used by QDSO or SMSQ(E) based
*   systems, and the necessary changes have not (yet at 
*   least) been implemented for the signal handling device.

*           moveq       #sms.info,d0
*           trap        #1
*           cmp.l       #'S2AT',(a0)            ; SMS2 based system?
*           bne.s       not_sms2                ; ... NO, so we are OK
*           suba.l      a0,a0
*           lea         sms2_text,a1
*           move.w      ut.wtext,a2
*           jsr         (a2)
*           moveq       #-19,d0
*           rts
*
* sms2_text: dc.w        sms2e-sms2t
* sms2t      dc.b        'Signal extension '
*            dc.b        'not yet validated for use on SMS2',10
* sms2e:
*            ds.w      0
*
* not_sms2:
           moveq       #dev_size,d1
           moveq       #sms.achp,d0            
           clr.l       d2
           trap        #1
           tst.l       d0
           bne.s       exit_ini
           move.l      a0,a4

           lea         sched,a1
           move.l      a1,dev_scadr(a0)
           lea         poll,a1
           move.l      a1,dev_pladr(a0)
           lea         open,a1
           move.l      a1,dev_open(a0)
           lea         io,a1
           move.l      a1,dev_io(a0)
           lea         close,a1
           move.l      a1,dev_close(a0)
           clr.l       dev_joblist(a0)         ; (-$18) init joblist   
           clr.l       dev_nxevent(a0)
           clr.l       dev_nticks(a0)

           lea         dev_link(a0),a0
           moveq       #sms.liod,d0
           trap        #1
           tst.l       d0
           bne.s       exit_ini
           lea         dev_scblk(a4),a0
           moveq       #sms.lshd,d0
           trap        #1
           lea         dev_plblk(a4),a0
           moveq       #sms.lpol,d0
           trap        #1

           suba.l      a0,a0
           lea         init_text,a1
           move.w      ut.wtext,a2
           jsr         (a2)
exit_ini:
           rts

version    equ         '0.27'

init_text: dc.w        ite-itsst
itsst      dc.b        'Signal extension v0.27a, '
           dc.b        ' Richard Zidlicky',10
ite:
           ds.w      0
           

***********************************************************
* devdriver code

******************
* poll routine : dev_gticks++, if dev_nticks!=0 then  dev_nticks--
poll:
           addq.l      #1,dev_gticks(a3)
           tst.l       dev_nticks(a3)
           beq.s       rtpoll
           subq.l      #1,dev_nticks(a3)
rtpoll:    rts

* sched routine : send signal if dev_nticks==0 && dev_nxevent!=0
sched:     tst.l       dev_nticks(a3)
           bne.s       rsc
           tst.l       dev_nxevent(a3)
           beq.s       rsc
           move.l      dev_nxevent(a3),a0
           addq.l      #ml,a0

           move.l      #'%MSG',msg.magic(a0)
           clr.w       msg.type(a0)            ; just a signal
           move.l      a0,a4                   ; arg for _send_msg
           suba.l      a5,a5
           moveq       #1,d3

           bsr         _send_msg               ; ... send the signal
           cmpa.l      #0,a5
           beq.s       scerr_spur              ; pretty bad ...

           addq.w      #TIMER,msg.type(a0)
           subq.l      #ml,a0

           move.l      ml.nxt(a0),ch_events(a5)            ; unlink
           move.l      msg.t_int+ml(a0),d0                 ; interval timer ?
           beq.s       scnoint

           move.l      d0,msg.t_ticks+ml(a0)   ; new tick count
           move.l      a0,a4
           clr.w       d7                      ; no response !
           bsr         insert_event            ; insert time sorted
scfe:      bsr         find_next_event         ; reschedule
           bra.s       sched                   ; more pending events?

rsc:       rts

scnoint:   move.l      a3,a4
           move.w      mm.rechp,a2
           jsr         (a2)
           move.l      a4,a3
           bra.s       scfe

* should not really happen, uncomment for debuging:
scerr_spur:
           clr.l       dev_nxevent(a3)         
           bra.s       rsc

*          suba.l      a0,a0
*          clr.l       dev_nxevent(a3)
*          lea         scerr_txt,a1
*          move.w      ut.wtext,a2
*          jsr         (a2)
*          rts
*scerr_txt:            dc.w        scete-scets
*scets:    dc.b        'signal extension:  timer ll corruption',10
*scete     ds.w      0


******************************************
open:
           move.l      a0,a2
           move.l      d1,d7                   ; save jobid
           move.w      (a2)+,d2                ; check name
           move.l      (a2)+,d1
           andi.l      #UCMASK0,d1 
           cmp.l       #'*SIG',d1
           bne.s       err_nf
           move.l      (a2)+,d1
           andi.l      #UCMASK1,d1 
           cmp.l       #'NAL_',d1
           bne.s       err_nf
           bra.s       nm_found
           dc.w        6
           dc.b        'SIGNAL'

nm_found:  subq.w      #8,d2
           beq.s       err_bp
           blt.s       err_nf
           subq.w      #1,d2
           bne.s       err_bn

           move.b      (a2)+,d1
           andi.b      #$df,d1

           cmp.b       #'S',d1                 ; send only
           beq         o_send_sig
           cmp.b       #'R',d1                 ; send & receive
           beq.s       o_sr_sig                ; init signal handler

err_bn:    moveq       #-12,d0
           rts
err_nf:    moveq       #-7,d0
           rts
err_bp:    moveq       #-15,d0
           rts
err_iu:    move.w      mm.rechp,a2
           jsr         (a2)                    ; release memory
           moveq       #-9,D0
           rts

* open send/receive
o_sr_sig:
           move.l      d3,d5                   ; save ihptr
           beq.s       err_bp                  ; no address-> no channel
           andi.b      #1,d3
           bne.s       err_bp                  ; uneven address
           move.l      a3,a5                   ; save
           move.w      mm.alchp,a2
           move.l      #ch_len,d1
           jsr         (a2)
           tst.l       d0
           bne.s       rtfo

           lea         dev_joblist-ch_nxtch(a5),a1         ; adjust to "previous"
loop:      move.l      ch_nxtch(a1),d0
           beq.s       eloop
           move.l      d0,a1
           cmp.l       ch_ojob(a1),d7          ; max one handler per job!
           beq.s       err_iu
           bra.s       loop
eloop:     move.l      a0,ch_nxtch(a1)

init_ch    move.l      d5,ch_sigh(a0)
           clr.l       ch_nxtch(a0)
           clr.l       ch_events(a0)
           moveq       #0,d0

** now check whether handler is '%csg', if so init some values
           move.l      d5,a1
           cmp.w       #$4afc,(a1)
           bne.s       rtfo                    ; should really give some warning
           cmp.l       #'%CSG',h_type(a1)
           bne.s       rtfo
           lea         c_sigvec,a2 ; vector for clib calls
           move.l      a2,hc_sigvec(a1)
           clr.l       hc_block(a1)
           clr.l       hc_pend(a1)
           clr.w       hc_activ(a1)            
***        clr.b       hc_defer(a1)
***        clr.b       hc_ixx(a1)
           clr.w       hc_defer(a1)            ; short for above 

rtfo:      rts

* open send only
o_send_sig:
           move.l      #ch_len,d1
           move.w      mm.alchp,a2
           jsr         (a2)
           clr.l       ch_sigh(a0)
           clr.l       ch_nxtch(a0)
           clr.l       ch_events(a0)
           bra.s       rtfo


******
close:
           movem.l     d4-d7/a4-a5,-(a7)
           move.l      a0,a5

           tst.l       ch_sigh(a0)
           beq.s       ecls                    ; send only, no unlink needed

           move.l      ch_sigh(a0),a1
           cmp.l       #'%CSG',h_type(a1)
           bne.s       cevnt
           clr.w       hc_activ(a1)            ; still could be inside handler
           clr.l       h_type(a1)

* cancel all ch_events, free their mem
cevnt:
           move.l      ch_events(a0),a4
           cmpa.l      #0,a4
           beq.s       unlink

           clr.l       ch_events(a0)
           move.l      a3,d4

l_rm:      move.l      a4,a0
           move.l      ml.nxt(a0),a4           ; next
           move.w      mm.rechp,a2
           jsr         (a2)                    ; release mem
           cmpa.l      #0,a4
           bne.s       l_rm

cl_erm:    move.l      d4,a3
           move.l      a5,a0

unlink:    move.l      dev_joblist(a3),d1
           beq.s       cret
           lea         dev_joblist-ch_nxtch(a3),a1         ; adjust to "last" ptr
cll:       cmp.l       d1,a0
           beq.s       eclsf
           move.l      d1,a1
           move.l      ch_nxtch(a1),d1
           bne.s       cll

cret       movem.l     (a7)+,d4-d7/a4-a5       
           rts                                 ; not in list ??

eclsf:
           move.l      ch_nxtch(a0),ch_nxtch(a1)           ; unlink channel

           bsr         find_next_event                     ; reschedule events
ecls:
           move.l      a5,a0
           movem.l     (a7)+,d4-d7/a4-a5
           move.w      mm.rechp,a2
           jmp         (a2)                    ; return memory& rts

           

**********
io:        
           cmp.b       #SEND.SIG,d0            ; old entry point?
           beq         send_sig
           moveq.l     #0,d7
           moveq.l     #0,d6
           and.l       #$ffff,d1
           and.l       #$ffff,d2
           cmp.b       #SR.MSG,d0
           beq.s       sr_msg
           move.l      d2,d5                   ; length
           cmp.b       #SEND.MSG,d0
           beq.s       send_msg

           cmp.b       #INFO,d0
           beq.s       info_msg
           moveq       #-15,d0
           rts
err_m_or:  moveq       #-4,d0
           rts

* io_fstrg parameters 
info_msg:
           moveq       #8,d0
           cmp.w       d0,d2
           bcs.s       erim
           move.w      d0,d1
imc:       lea         8+dev_scratch(a3),a4
           move.l      dev_gticks(a3),-(a4)
           move.l      #version,-(a4)          
           subq.w      #1,d0
imcc:      move.b      (a4)+,(a1)+
           dbra        d0,imcc
           clr.w       d0
           rts

erim:      moveq       #-5,d0      ; buffer full
           rts


* io_edlin parameters
sr_msg:    suba.l      d1,a1                   ; start of message
           st          d7                      ; flag: return value wanted
           move.l      d1,d5                   ; actual length

* io_sstrg parameters
send_msg:
           lea         dev_scratch(a3),a4
           moveq       #msg_maxlen-1,d4
           move.l      a1,a2
smcpm:     move.b      (a2)+,(a4)+ ; copy to scratch to insure
           dbra        d4,smcpm                ; word alignment
           
           lea         dev_scratch(a3),a4
           tst.b       d7
           bne.s       _smsg
           move.l      msg.len(a4),d6          ; len for io.sstrg return
_smsg:     cmp.w       #msg_minlen,d5          ; too short
           bcs         err_nomsg
           move.l      msg.len(a4),d4
           cmp.w       d4,d5                   ; complete msg must be in buffer
           bcs         err_nomsg
           cmp.w       d4,d2
           bcs         err_nomsg

_send_msg:
           movem.l     d1-d2/a0/a1/a3,-(a7)
           cmp.l       #'%MSG',msg.magic(a4)
           bne         err_blmsg
           tst.w       msg.type(a4)
           bne.s       set_alarm

           movem.l     d6/d7/a1,-(a7)
           move.l      msg.signr(a4),d2
           move.l      msg.jobid(a4),d1
           move.l      msg.uval(a4),a2
           move.l      msg.prio(a4),a1

           bsr         send_sig
           movem.l     (a7)+,d6/d7/a1
           bra.s       sm_ret

set_alarm:
           cmp.l       #tmsg_len,msg.len(a4)
           bcs         err_nomsg
           cmp.w       #TIMER,msg.type(a4)
           bne.s       err_msg_ni
           moveq       #0,d2                   ; signr := test
           move.l      msg.jobid(a4),d1

           movem.l     d6/d7/a4,-(a7)
           move.l      msg.prio(a4),a1
           bsr         send_sig                ; test job and handler
           movem.l     (a7)+,d6/d7/a4
           move.l      d1,msg.jobid(a4)        ; real jobid if jobid=-1
           movem.l     (a7),d1-d2/a0/a1/a3

* a5= chdefbl for jobid
           cmp.w       #-20,d0
           beq.s       csam                    ; ignore low priority
           tst.w       d0
           bne.s       sm_ret

csam:      subq.l      #ml,a4                  ; point to linkage block
           tst.l       msg.signr+ml(a4)
           beq.s       mscnt                   ; cancel request; dont alloc
           move.l      msg.len+ml(a4),d1
           addq.l      #ml,d1                  ; extra space for link pointer
           move.w      mm.alchp,a2
           jsr         (a2)
           tst.w       d0
           bne.s       sm_ret

           addq.l      #ml,a0
           addq.l      #ml,a4
           move.l      msg.len(a4),d2
           move.l      a4,a1
           move.l      a0,a4                   ; save addr of block
           subq.l      #1,d2
cpymsg:    move.b      (a1)+,(a0)+
           dbra        d2,cpymsg
           subq.l      #ml,a4                  ; point to msg-4

* a4=lmsg, a3=dev, a5=chdef
mscnt:     movem.l     (a7),d1-d2/a0/a1/a3
           bsr.s       insert_event
           movem.l     (a7),d1-d2/a0/a1/a3
           bsr         find_next_event         ; reschedule events
           clr.l       d0                      ; no error

sm_ret     movem.l     (a7)+,d1-d2/a0/a1/a3
           tst.w       d0
           bne.s       srs
           move.l      d6,d1                   ; msg.len or old msg.len
sr1:       adda.l      d1,a1                   ; behave like a real io.sstrg
sr2:       rts                                 ;   or io.edlin

srs:       tst.w       d7
           bne.s       sr1
           rts

err_msg_ni:
           moveq       #-11,d0                 ; err.bn
           bra.s       sm_ret
err_nomsg:
           moveq       #-15,d0                 ; err.bp
           bra.s       sm_ret
err_blmsg:
           moveq       #-21,d0
           bra.s       sm_ret

* subr: a4 msg-4, a3 dev_df a5 ch_def d7 flag -> copy old event to (a1),d6 len
* signr==0 : cancel previous event
insert_event:
           tst.w       msg.txi+ml(a4)          ; unit?
           beq.s       cticks
           move.w      ticks_per_second,d0
           move.l      msg.t_ticks+ml(a4),d1   
           bsr         mul16x32
           move.l      d1,msg.t_ticks+ml(a4)
           move.l      msg.t_int+ml(a4),d1
           bsr         mul16x32
           move.l      d1,msg.t_int+ml(a4)
cticks:    
           move.l      dev_gticks(a3),d0
           add.l       d0,msg.t_ticks+ml(a4)   ; form now on
           move.l      msg.t_evid+ml(a4),d1
           lea         ch_events(a5),a2        ; 'previous'
iltev:     move.l      (a2),d0
           beq.s       insrt
           move.l      d0,a0                   ; search for old entry
           cmp.l       msg.t_evid+ml(a0),d1    ; event id
           beq.s       rmv_oldevent
           lea         ml.nxt(a0),a2
           bra.s       iltev

rmv_oldevent:
           move.l      ml.nxt(a0),(a2)         ; unlink
           tst.b       d7
           beq.s       nmcp                    ; no response wanted
           lea         ml(a0),a2
           move.l      dev_gticks(a3),d1
           sub.l       d1,msg.t_ticks(a2)      ; remaining time
           tst.w       msg.txi(a2)
           beq.s       nocnv
           move.w      ticks_per_second,d0
           move.l      msg.t_ticks(a2),d1      ; convert rest time
           bsr.s       div16x32
           tst.l       d1
           bne.s       cnvnz
           moveq       #1,d1                   ; must be at least '1'
cnvnz:     move.l      d1,msg.t_ticks(a2)
           move.l      msg.t_int(a2),d1
           bsr.s       div16x32
           move.l      d1,msg.t_int(a2)

nocnv:     move.l      msg.len(a2),d6
           subq.l      #1,d6
           move.l      #'%MSG',msg.magic(a2)   ; return with correct magic
cpml:      move.b      (a2)+,(a1)+ 
           dbra        d6,cpml
           move.l      msg.len+ml(a0),d6
nmcp:      move.w      mm.rechp,a2
           move.l      a3,-(a7)
           jsr         (a2)                    ; free mem
           move.l      (a7)+,a3

insrt:     tst.l       msg.signr+ml(a4)        ; BUGFIX, insrt label moved
           beq.s       ievret                  ; return, was a cancel/info request

           lea         ch_events(a5),a2        ; now insert into time sorted list
           move.l      msg.t_ticks+ml(a4),d1
istev:     move.l      (a2),d0
           beq.s       link_in
           move.l      d0,a0
           cmp.l       msg.t_ticks+ml(a0),d1
           bcs.s       link_in
           lea         ml.nxt(a0),a2
           bra.s       istev

link_in:   move.l      a4,(a2)
           move.l      d0,ml.nxt(a4)           ; next event pointer
           clr.l       ml+msg.magic(a4)        ; clear magic to keep
ievret     rts                                 ; clean heap entry 


mul16x32:
           move.l      d1,d2
           clr.w       d2
           swap        d2
           mulu        d0,d2
           swap        d2                      ; ignore overflow
           clr.w       d2
           mulu        d0,d1
           add.l       d2,d1
           rts

div16x32:  
           divu        d0,d1
           andi.l      #$ffff,d1
           rts


* a3 device def. block 
find_next_event:
           suba.l      a1,a1
           moveq.l     #-1,d0                  ; unisgned, max tick count
           moveq.l     #0,d4
           move.l      dev_joblist(a3),a2
           move.l      a2,d3
           beq.s       fnele
fnel1:
           move.l      ch_events(a2),a0
           cmpa.l      d4,a0
           beq.s       fnel1nx                 ; no events for this channel
           move.l      msg.t_ticks+ml(a0),d1
           cmp.l       d0,d1
           bcs.s       fnnm
fnel1nx:   move.l      ch_nxtch(a2),d3
           beq.s       fnele
           move.l      d3,a2
           bra.s       fnel1

fnnm:      move.l      a0,a1                   ; new ticks-min
           move.l      d1,d0
           bra.s       fnel1nx

fnele:     move.l      a1,dev_nxevent(a3)
           sub.l       dev_gticks(a3),d0
           bcc.s       fnnz                    ; >=0
           clr.l       d0
fnnz:      move.l      d0,dev_nticks(a3)
           rts


* d1=jobid, d2.l=interruptnr, d3=tmout, a1= priority, a2=usr_value

*********
* verify jobid, find handler, send signal(s)

send_sig:  
           move.l      a2,d7                   ; save uval
           lea         -tmp_size(a7),a7
           move.l      a7,a2
           cmp.l       #-1,d1
           bne.s       checkjob
           move.l      sv_jbpnt(a6),a4         ; negative jobid
           move.l      a4,d1
           sub.l       sv_jbbas(a6),d1
           asr.l       #2,d1
           swap        d1
           move.l      (a4),a4
           move.w      job.tag(a4),d1
           swap        d1

checkjob:
           movem.l     d1/a0-a1,-(a7)          ; d1=jobid
           move.l      d1,d4
           beq         ix_bj                   ; don't send signal to job 0
           clr.l       d1
           move.w      d4,d1
           asl.l       #2,d1
           move.l      sv_jbbas(a6),a0
           adda.l      d1,a0
           cmpa.l      sv_jbtop(a6),a0
           bcc         ix_bj
           tst.b       (a0)                    ; job removed ?
           bmi         ix_bj
           move.l      (a0),a0                 ; get jobheader
           move.l      d4,d1
           swap        d1
           cmp.w       job.tag(a0),d1
           bne         ix_bj
           move.l      d4,d1
                                                           
check_d3:
           tst.l       d3
           beq.s       oj                      ; is it current job ?
roj:       move.l      dev_joblist(a3),a3
           move.l      a3,d3
           beq         ix_nf

* search for handler
ssfcl:
           cmp.l       ch_ojob(a3),d1          ; search for sighandler
           beq.s       found
           move.l      ch_nxtch(a3),d3
           beq         ix_nf
           move.l      d3,a3
           bra.s       ssfcl       

oj:
           move.l      sv_jbpnt(a6),a4         ; BUGFIX ??
           cmpa.l      (a4),a0
           bne.s       roj
           tst.l       d2
           beq.s       roj                     ; only testing job & handler
           bra         ix_nc



**********
* now handle the different handlers

found:
           move.l      a1,d6                   ; save priority
           move.l      d1,d4                   ; jobid
           move.l      d2,d5                   ; signalnr

           move.l      a3,a5                   ; ch-def-block
           move.l      ch_sigh(a5),a4          
           cmp.w       #$4afc,(a4)
           bne         ix_bl
           cmp.l       #'%SIG',h_type(a4)
           bne         try_csig

******************
* '%SIG' specific
*
           move.l      h_type(a4),tmp.htype(a2)
           tst.l       d2                      ; signr==0 
           beq.s       test_ok
           bmi         ix_ni                   ; signr<=0 ??
           tst.l       hs_handler(a4)
           beq         ix_nc

           move.l      d6,d0
           andi.l      #P_RES_S,d0 ; some unimplemented bits ?
           bne         ix_ni
           move.l      hs_prio(a4),tmp.prio(a2)
           move.l      hs_handler(a4),tmp.haddr(a2)
           move.l      hs_stackmin(a4),tmp.stack(a2)

           bsr         sub_penq

* do_int | set job_stat&do_int | err_nc | err_smp
* if d0=sgn_def : clr d0, just proceed
* if d0<0 : bra end, error
* if d0>=0 : do_int

           cmp.w       #sgn_def,d0
           beq.s       cd0
           tst.w       d0
           bmi         end
nxi        
           bra         do_int

cd0:       clr.l       d0
           movem.l     d0-d2,tmp.sregs(a2)
           bra.s       nxi

test_ok:   clr.l       d0
           bra         end

*********************
* '%CSG' specific
* subroutine to check blocking conditions and evaluate
* handler information 
cseval:
           cmp.l       #'%CSG',h_type(a4)
           bne         cse_err.ni
           move.l      h_type(a4),tmp.htype(a2)
           move.l      hc_stack(a4),tmp.stack(a2)
           cmpi.l      #32,d5                  ; signr>32 ?
           bhi         cse_err.ni
           cmp.l       hc_nsigs(a4),d5
           bhi.s       cse_err.or
           tst.l       d5                      ; signr==0 (test job&handler)
           beq         csdone
           bmi.s       cse_err.ni              ; signr<=0 ??

           move.l      d5,d2
           asl.l       #2,d2
           tst.w       hc_priid(a4)
           bne         getpri
           move.l      hc_prio(a4),tmp.prio(a2)
csfh:
* default/current/ignored ?
           cmp.l       #SIGKILL,d5
           beq         default                 ; ... always do default action
           move.l      hc_curr(a4),a1
           move.l      0(a1,d2.l),d0           ; current handler
           beq         ignore                  ; ignore signal
           cmp.l       #SIG_DFL,d0
           beq         default

bch:       move.l      d0,tmp.haddr(a2)

           moveq.l     #1,d0
           move.l      d5,d2
           subq.l      #1,d2
           asl.l       d2,d0                   ; sigmask
           move.l      d0,d3                   ;   save it for a moment

           tst.w       tmp.force(a2)
           bne.s       cseforce                ; ignore any blocking

           and.l       hc_block(a4),d0         ; blocked?
           and.l       #NOBLOCKMASK,d0
           bne         is_blocked

           tst.w       hc_protid(a4)
           bne         getprotlevel            ; array ?
           move.l      hc_protlev(a4),d1       ; single value
cmpprlev:  cmp.w       hc_chlev(a4),d1
           bhi         is_blocked
           
cseforce   not.l       d3
           and.l       d3,hc_pend(a4)          ; delete any pending instances

           clr.l       d0          ; proceed
cseret     rts


cse_err.ni moveq       #-19,d0
           rts
cse_err.or moveq       #-4,d0
           rts

rpri:      move.l      hc_prio(a4),a1
           move.l      d5,d2
           asl.l       #2,d2
           move.l      0(a1,d2.l),tmp.prio(a2)
           bra.s       csp




try_csig:
           move.l      d6,d0
           andi.l      #P_RES_S,d0 ; any unimplemented bits ?
           bne         ix_ni

           cmp.l       #'%CSG',h_type(a4)
           bne         ix_ni

           tst.l       d5
           beq         test_ok

           tst.w       hc_priid(a4)
           bne.s       rpri
           move.l      hc_prio(a4),tmp.prio(a2)

csp:       bsr         sub_penq
           tst.w       d0
           bmi         end                     ; priority too small ?
           movem.l     d0-d2,tmp.sregs(a2)     ; save results..

           clr.w       tmp.force(a2)
           bsr         cseval
           cmp.l       #-1,d0
           beq         test_ok     ; report sucess
           blt         end         ; report error

           tst.b       hc_ixx(a4)
           bne         cs_blocked              ; checksig in progress
           movem.l     tmp.sregs(a2),d0-d2
           cmp.w       #sgn_def,d0
           beq.s       cs_def
           tst.w       d0
           bgt.s       cs_sstat
cs_doint:  
           bra         do_int

cs_sstat:
           tst.b       hc_defer(a4)
           beq.s       cs_doint
           bsr         sub_chaddr              ; test handler address
           cmp.w       #-1,d0
           beq         test_ok
           blt         end                     ; error

* reuse frame of defered signal:
           clr.b       hc_defer(a4)
           move.l      job.a7(a0),a1
           move.l      tmp.haddr(a2),(a1)      ; substitute new handler
           lea         4(a1),a1                ; addr of frame to be reused
           movem.l     tmp.sregs(a2),d0-d2
           move.w      d0,svb.sgn(a1)
           move.l      d1,svb.fs1(a1)
           move.l      d2,svb.fs2(a1)

           move.l      esvb.signr(a1),d2       ; get old values
           move.l      esvb.uval(a1),d0

           move.l      tmp.prio(a2),job.d5(a0)
           move.l      d5,job.d4(a0)
           move.l      d7,job.a4(a0)
           move.l      d5,esvb.signr(a1)
           move.l      d7,esvb.uval(a1)
           move.l      d2,d5
           move.l      d0,d7
           bsr.s       is_blocked              ; mark old as pending
           movem.l     tmp.sregs(a2),d0-d2
           bsr         sub_sysci
           bra         test_ok


cs_def:    tst.b       hc_defer(a4)            ; defer max one
           bne.s       cs_blocked
           st          hc_defer(a4)
           clr.l       d0
           movem.l     d0-d2,tmp.sregs(a2)
           bra         cs_doint

cs_blocked:
           bsr.s       is_blocked
           bra         test_ok

getprotlevel:
           move.l      d5,d2
           asl.l       #1,d2                   ; array of words
           move.l      hc_protlev(a4),a1
           move.w      0(a1,d2.l),d1
           bra         cmpprlev

getpri:    move.l      hc_prio(a4),a1
           move.l      0(a1,d2.l),tmp.prio(a2)
           bra         csfh

suval:     move.l      d0,a3
           move.l      d5,d0
           asl.l       #2,d0
           move.l      d7,0(a3,d0.l)
           bra.s       ib1

is_blocked:
           move.l      hc_uval(a4),d0
           bne.s       suval
ib1:       moveq.l     #1,d0
           move.l      d5,d2
           sub.l       d0,d2
           asl.l       d2,d0
           or.l        d0,hc_pend(a4)          ; mark pending
           bra.s       csdone                  ; no further action needed

default:   move.l      hc_def(a4),d1
           beq.s       sysdefl
           move.l      d1,a1
           move.l      0(a1,d2.l),d0
           beq.s       ignore                  ; SIG_IGN
           bra         bch

sysdefl:   moveq       #SIG_DFL,d0
           bra         bch


ignore:    moveq.l     #1,d0
           sub.l       d0,d5                   ;  reset pending
           asl.l       d5,d0                   ; sigmask
           not.l       d0
           and.l       d0,hc_pend(a4)
csdone     moveq.l     #-1,d0
           rts




************************
* now do the software interrupt
*

* subr, alloc stack and store signature
setupframe:
           move.l      job.a7(a0),a1
           cmp.l       #'%CSG',tmp.htype(a2)
           bne.s       simpleframe
           suba.l      #esvb_len,a1
           bra.s       cofr

simpleframe:
           suba.l      #svb_len,a1 ; adjust to frame
cofr:      cmpa.l      tmp.stack(a2),a1        ; enough stack for interrupt?
           bcs.s       err.nostack ; blt
           move.w      d0,svb.sgn(a1)          ; set frame type
           move.l      d1,svb.fs1(a1)
           move.l      d2,svb.fs2(a1)
           rts

err.nostack:
           addq.l      #4,a7
           moveq       #-3,d0                  ; err.om
           bra         end

sub_sysci: cmp.w       #sgn_wfj,d0
           bne.s       cssci
           move.l      d2,a3
           clr.b       job.wflag(a3)
cssci:     move.w      #1,job.stat(a0)
           rts

sub_chaddr:
           move.l      tmp.haddr(a2),d3        ; extra check for ignore/default
           beq.s       cha_ign                 ; ->ignore
           moveq       #1,d0
           cmp.l       d0,d3                   ; =1 ?
           beq.s       disysd                  ; -> SIG_DFL
           and.l       d0,d3                   ; uneven address ?
           bne.s       cha_bl
           bra.s       cha_ret
* do system default action
disysd:    cmp.b       sysdeftab,d5
           bcc.s       cha_ign
           lea         sysdeftab,a3
           move.b      0(a3,d5.w),d0
           beq.s       cha_ign
dik:       lea         killjob,a3
           move.l      a3,tmp.haddr(a2)
cha_ret:   clr.l       d0
           rts
cha_ign:   moveq.l     #ERR.NC,d0
           rts
cha_bl:    moveq.l     #ERR.BL,d0
           rts

* main entry point
do_int:
           bsr.s       sub_chaddr              ; check handler address
           cmp.w       #-1,d0
           beq.s       eret                    ; ignore
           blt.s       end                     ; other error

           movem.l     tmp.sregs(a2),d0-d2
           bsr         setupframe
           tst.w       d0
           ble.s       csdi
           bsr.s       sub_sysci               ; interrupt  system call
csdi:
           move.l      job.pc(a0),svb.ret(a1)  ; save PC
           move.w      job.sr(a0),svb.sr(a1)               ; save SR
           move.l      job.a7(a0),svb.a7(a1)               ; for REQSIGSTACK
           move.l      job.d4(a0),svb.d4(a1)               ; save D4-D6
           move.l      job.d5(a0),svb.d5(a1)
           move.l      job.d6(a0),svb.d6(a1)
           move.l      job.a4(a0),svb.a4(a1)
           move.l      job.a5(a0),svb.a5(a1)               ;  .... A4/A5

           move.l      d5,job.d4(a0)                       ; interrupt number
           move.l      d7,job.a4(a0)                       ; usr val
           move.l      d6,job.d5(a0)                       ; priority

           cmp.l       #'%CSG',tmp.htype(a2)
           beq.s       csg_setpc

           lea         int_ret,a3
           move.l      a3,svb.finish(a1)
           move.l      a1,job.a7(a0)                       ; adjust jobs SP
           move.l      tmp.haddr(a2),job.pc(a0)            ; handler routine
eret:      
           clr.l       d0
end:       
           movem.l     (a7)+,d1/a0-a1
           lea         tmp_size(a7),a7
           rts


csg_setpc: 
           lea         cint_ret,a3
           move.l      a3,svb.finish(a1)       ; cleanuproutine address
           move.l      a4,esvb.hstruct(a1)     ; some more info
           move.l      d5,esvb.signr(a1)       ;  for handler restore
           move.l      d7,esvb.uval(a1)        ;  and reset

           move.l      tmp.haddr(a2),-(a1)
           move.l      a1,job.a7(a0)                       ; adjust SP
           lea         ccal_setup,a3
           move.l      a3,job.pc(a0)           ; wrapper for c68 calls

           clr.l       d0
           bra.s       end


ix_nf      moveq       #-7,d0
           bra         end

ix_bl:     moveq       #-21,d0
           bra         end
ix_ni      moveq       #-19,d0
           bra         end
ix_bj      moveq       #-2,d0
           bra         end
ix_or      moveq       #-4,d0
           bra         end

ix_nc      moveq       #-1,d0
           bra         end




*****************
* sub_penq : check if job can be interrupted
*   returns d0, d1=svb.sf1, d2=svb.sf2
*           d0 = sgn_def : defer after syscall
*           d0 = -1       : err_nc
*           d0= -20       : error, priority to small
*           d0 >= 0       : interrupt syscall

sysc_def   equ         $1000

sub_penq:
           move.w      job.sr(a0),d0
           andi.w      #SUPER,d0               ; job in sv mode?
           bne.s       ret_nc                  ; must wait

           move.l      tmp.prio(a2),d3         ; required priority
           andi.l      #P_RES_R,d3 ; some unimplemented features ?
           bne.s       ret_ni
           move.l      tmp.prio(a2),d3
           move.l      d6,d0                   ; priority of interrupt
           
           tst.w       job.stat(a0)
           bne         stnook                  ; test some conditions

           andi.w      #NORM,d3
           andi.w      #NORM,d0
           cmp.w       d3,d0
           ble         ret_smp                 ; priority too small
           clr.l       d0
           clr.l       d1
           clr.l       d2
           rts

ret_nc:    moveq.l     #-1,d0
           rts
ret_ni:    moveq.l     #-19,d0
           rts

wj:        andi.w      #WFJOB,d0
           andi.w      #WFJOB,d3
           cmp.w       d3,d0
           bgt.s       wji
           move.l      tmp.prio(a2),d3         ; required priority to signal
           move.w      d6,d0
           move.w      d6,d1
           andi.w      #DF_WJOB,d1
           bne         ret_def                 ; defer signal after job released
           bra         ret_smp

* deal with jobs waiting for another job
* first find that other job:
wji:       move.l      sv_jbbas(a6),a1                     ; base of jobtab
           move.l      sv_jbtop(a6),d3                     ; top
jslo       cmp.l       a1,d3
           bcs.s       ret_errbj
           move.l      (a1)+,a3
           move.l      a3,d0
           bmi.s       jslo
           tst.b       job.wflag(a3)
           beq.s       jslo
           cmp.l       job.wjob(a3),d4
           beq.s       jobfound
           bra.s       jslo

ret_errbj: moveq.l     #ERR.NJ,d0
           rts                                 ; shouldn't ever happen

jobfound:
           move.l      a1,d0                   ; compute jobtable index
           sub.l       sv_jbbas(a6),d0
           asr.l       #2,d0
           subq.w      #1,d0                   ;
           swap        d0
           move.w      job.tag(a3),d0
           swap        d0                      ;    to get jobid

* SAVE STATE INFO
           move.l      d0,d1                   ; save jobid
           move.l      a3,d2
           moveq.l     #sgn_wfj,d0
           rts

t3:        andi.w      #WFIO,d0
           andi.w      #WFIO,d3
           cmp.w       d3,d0       
           bgt.s       schan
           move.l      tmp.prio(a2),d3
           move.w      d6,d1
           move.w      d6,d0
           andi.w      #DF_WIO,d1              ; defer signal after completion 
           bne         ret_def
           bra         ret_smp


* try to figure out why job.stat is set and handle it
stnook:    
           cmp.w       #-2,job.stat(a0)        ; waiting for another job ?
           beq         wj                      ; ... check if we should signal

* ... assume WFIO and try to verify
* by examining a0

schan:     move.l      sv_chbas(a6),a1
           move.w      2+job.a0(a0),d0         ; chnr
           cmp.w       sv_chmax(a6),d0
           bhi.s       suspcheck
           asl.w       #2,d0                   ; *4
           adda.w      d0,a1
           tst.b       (a1)
           bmi.s       suspcheck
           move.l      (a1),a3                 ; ch.defblock  -> a3
           move.w      job.a0(a0),d0
           cmp.w       ch_tag(a3),d0           ; another channel with same
           bne.s       suspcheck               ; index in chan table?

           tst.b       ch_stat(a3)
           bge.s       suspcheck
           cmp.l       ch_jobwt(a3),d4         ; jobid matches?
           bne.s       suspcheck
cnfound:   
           move.l      tmp.prio(a2),d3
           move.l      d6,d0
           move.l      d6,d1
           andi.w      #WFIO,d0
           andi.w      #WFIO,d3
           cmp.w       d3,d0
           bgt.s       t3_int
           move.w      d6,d1
           andi.w      #DF_WIO,d1              ; can it be defered ?
           bne.s       ret_def
           bra.s       ret_smp

t3_int:
           moveq.l     #sgn_ios,d0
           move.l      job.a0(a0),d1           ; save chid
           clr.l       d2
           move.b      ch_actn(a3),d2          ; save action
           rts


suspcheck:
           move.l      d6,d0                   ; send priority
           move.l      d6,d1

           move.l      tmp.prio(a2),d3         ; required priority
           andi.w      #SUSP,d0                
           andi.w      #SUSP,d3                
           cmp.w       d3,d0
           bgt.s       int_susp    
           move.w      d6,d0
           andi.w      #DF_SUSP,d1
           bne.s       ret_def                 ; defer signal after syscall
           bra.s       ret_smp

           
int_susp:
           moveq.l     #sgn_sus,d0
           clr.l       d1
           clr.l       d2
           rts

ret_smp:
           moveq.l     #-20,d0
           rts

ret_def:   move.l      tmp.prio(a2),d3
           move.l      d6,d0                   ; priority of interrupt
           andi.w      #NORM,d3
           andi.w      #NORM,d0
           cmp.w       d3,d0
           ble         ret_smp                 ; too small even to defer
           moveq.l     #sgn_def,d0
           clr.l       d1
           clr.l       d2
           rts




***********************************************************************

* standard cleanup, restores jobs stack, registers and sr flags
* tested only on 68008

*int_ret:
*          lea         -4(a7),a7
*_iret_:   trap        #0
*          move.l      USP,a5
*          move.l      svb.a7(a5),a4
*          move.l      a4,USP
** if not g.o. 68000: move.w       #0,(-a7)  * exception format word
*          move.l      svb.ret(a5),-(a7)
*          move.w      svb.sr(a5),-(a7)
*          move.l      svb.d4(a5),d4
*          move.l      svb.d5(a5),d5
*          move.l      svb.d6(a5),d6
*          move.l      svb.a4(a5),a4
*          move.l      svb.a5(a5),a5
*          rte

* old version, can't relocate stack, but then
* it should work on all 680x0 chips
int_ret:
           lea         -4(a7),a7
           move.l      svb.a5(a7),a5
           move.l      svb.a4(a7),a4
           move.l      svb.d6(a7),d6
           move.l      svb.d5(a7),d5
           move.l      svb.d4(a7),d4
           lea         svb.sr(a7),a7
           rtr                     ; ccr restored, sr must be preserved

** return from C68 signal handler
cint_ret:
           lea         -4(a7),a7               ; adjust esvb frame
* restore mask&handler
           move.l      esvb.hstruct(a7),a1
           tst.w       hc_activ(a1)            ; sigaction() called 
           beq.s       noreset                 ; .. in the meantime ?

           subq.w      #1,hc_activ(a1)
           move.l      esvb.oblock(a7),hc_block(a1)        ; restore mask
           move.l      hc_curr(a1),a0
           move.l      esvb.signr(a7),d0
           asl.l       #2,d0
           move.l      esvb.ohand(a7),0(a0,d0.l)           ; restore handler
           
noreset:
           cmp.w       #sgn_raise,svb.sgn(a7)  ; gets raised anyway
           beq.s       ncs                                 ; no need for recursion
           movem.l     d3/a2/a3/d7/a5,-(a7)
           move.l      a1,a4                               ; handler pointer
           bsr         _checksig
           movem.l     (a7)+,d3/a2/a3/d7/a5

ncs:       movem.l     esvb.d0(a7),d0-d2/a0/a1 ; restore scratch regs
**         bra.s       _iret_      ; requires stack relocation, not yet
** tested with all 680x0 chips, so use this hack instead:
           move.l      svb.a5(a7),a5
           move.l      svb.a4(a7),a4
           move.l      svb.d6(a7),d6
           move.l      svb.d5(a7),d5
           move.l      svb.d4(a7),d4
           move.w      svb.sr(a7),esvb.sr(a7)
           move.l      svb.ret(a7),esvb.ret(a7)
           lea         esvb.sr(a7),a7
           rtr                     ; ccr restored, sr must be preserved

** wrapper to allow c-handlers
* first 2 instructions critical
ccal_setup:
           move.l      esvb.hstruct+4(a7),a5
           clr.b       hc_defer(a5)
* now save compiler scratch regs
           movem.l     d0-d2/a0/a1,esvb.d0+4(a7)
           move.l      (a7)+,a0                            ; handler address
           move.l      a7,a1                               ; esvb
           move.l      a4,-(a7)                            ; usrval
           move.l      d5,-(a7)                            ; priority
           move.l      d4,-(a7)                            ; signr
           move.l      a5,a4                               ; ptr to %CSG struct

           move.l      d4,d0                               ; signr
           asl.l       #2,d0                               ;   *size

           addq.w      #1,hc_activ(a4)                     ; handling in progress
           move.l      hc_block(a4),esvb.oblock(a1)        ; save procmask
           move.l      hc_curr(a4),a5
           move.l      0(a5,d0.l),esvb.ohand(a1)           ; save handler
           move.l      hc_samask(a4),a5
           move.l      0(a5,d0.l),d1
           or.l        d1,hc_block(a4)                     ; new procmask

           moveq.l     #1,d2
           sub.l       d2,d4
           asl.l       d4,d2                               ; sigmask
           and.l       hc_reset(a4),d2                     ; reset handler?
           beq.s       cssc
           lea         hc_curr(a4),a5
           move.l      #SIG_DFL,0(a5,d0.l)                 ; reset it
cssc:
           jsr         (a0)        ; call handler
           lea         12(a7),a7
           rts

*******************************************
* system default table used by sub_chaddr
* known actions are
* 0= ignore, 1= kill
* signals>15 are ingored by default

sysdeftab: dc.b        15          ; table length
           dc.b        1           ; SIGHUP
           dc.b        1           ; SIGINT
           dc.b        1           ; SIGQUIT 
           dc.b        1           ; SIGILL 
           dc.b        1           ; SIGPIPE
           dc.b        1           ; SIGSEGV  
           dc.b        1           ; SIGBUS   
           dc.b        0           ; SIGFPE 
           dc.b        1           ; SIGKILL
           dc.b        1           ; SIGALRM
           dc.b        1           ; SIGABRT 
           dc.b        1           ; SIGTRACE 
           dc.b        0           ; SIGWINCHD 
           dc.b        0           ; SIGWMREQ 
           dc.b        1           ; SIGTERM 
           dc.w        0


* default handler special cases

killjob:   moveq       #5,d0
           moveq       #-1,d1
           moveq       #EINTR,d3
           trap        #1
           rts

*******************************************

**** c-interface 
** parameter passing through stack

* c_sigvec(0,channel)              : sigcleanup()
* c_sigvec(1,channel,signr,oadr,nadr)          : sigaction(signr,oadr,nadr)
* c_sigvec(2,channel,how,omask,mask)           : c/e block mask
* c_sigvec(3,channel,mask)                     : examine pending
* c_sigvec(4,channel,signr,uval)   : raise() signr,uval
* c_sigvec(5,channel,signr,uval) : fraise()
* c_sigvec(6,channel)              : checksig(), raise pending signals

MAXSIGV    equ         6
PAR1       equ         12+52
PAR2       equ         16+52
PAR3       equ         20+52

c_sigvec:
           movem.l     d1-d7/a0-a5,-(a7)       ; (7+6)*4 = 52
           move.l      52+8(a7),d4 ; opcode
           bmi         svnoimp
           cmp.w       #MAXSIGV,d4
           bgt         svnoimp
           lea         sysvar_base,a5
           tst.l       (a5)
           beq.s       gsysvar
           move.l      (a5),a0
svc:       move.l      52+4(a7),d1 ; channel   
           move.l      d1,d2
           cmp.w       sv_chmax(a0),d1
           bhi         sverr.no
           asl.l       #2,d1
           move.l      sv_chbas(a0),a1
           adda.w      d1,a1
           tst.b       (a1)
           bmi         sverr.no
           move.w      sr,d7
           trap        #0                      ; -> privileged
           tst.b       (a1)
           bmi.s       su_err.no
           move.l      (a1),a1
           swap        d2
           cmp.w       ch_tag(a1),d2
           bne.s       su_err.no
           move.l      ch_driver(a1),a5
           lea         io,a2
           cmp.l       4(a5),a2    ; is it really this channel?
           bne.s       su_err.no
           move.l      ch_sigh(a1),a0
           move.w      d7,sr                   ; -> normal

           move.l      a0,a4
           cmp.l       #'%CSG',h_type(a4)
           bne         sverr.bp

           asl.l       #2,d4
           jmp         table(pc,d4.l)

su_err.no  move.w      d7,sr
           bra         sverr.no    

gsysvar:   moveq       #sms.info,d0
           trap        #1
           move.l      a0,(a5)
           bra.s       svc

* a1 chdefblock
table:     bra         sigcleanup
           bra         sigact
           bra         sigprocmask
           bra         sigpending
           bra         raise
           bra         fraise
           bra         checksig


sigcleanup:
***        clr.b       hc_defer(a0)
***        clr.b       hc_ixx(a0)
           clr.w       hc_defer(a0)            ; instead of above
           clr.w       hc_activ(a0)
           bra         checksig

SV_RESETHAND equ       1
sigact:
           move.l      PAR1(a7),d0
           move.l      d0,d1
           cmp.l       hc_nsigs(a0),d0
           bhi         sverr.or
           move.l      PAR3(a7),d2 ; old value of sigaction
           beq.s       svnact
           move.l      d2,a2
           asl.l       #2,d0
           move.l      hc_curr(a0),a3
           move.l      0(a3,d0.l),(a2)         ; sa_handler
           move.l      hc_samask(a0),a3
           move.l      0(a3,d0.l),4(a2)        ; sa_mask
           clr.l       8(a2)                   ; sa_flags
           moveq.l     #1,d3
           sub.l       d3,d1
           asl.l       d1,d3
           and.l       hc_reset(a0),d3
           beq.s       svnact
           move.l      #SV_RESETHAND,8(a2)     ; set reset handler flag
svnact     move.l      PAR2(a7),d2
           beq.s       sigvecret
           clr.w       hc_activ(a0)
           move.l      d2,a2
           move.l      PAR1(a7),d0
           move.l      d0,d1
           asl.l       #2,d0
           moveq.l     #1,d3
           sub.l       d3,d1
           asl.l       d1,d3
           move.l      hc_samask(a0),a3
           move.l      4(a2),0(a3,d0.l)
           not.l       d3
           and.l       d3,hc_reset(a0)
           and.l       d3,hc_block(a0)
           and.l       d3,hc_pend(a0)
           cmp.l       #4*NOCATCH,d0
           beq.s       sigvecret
           move.l      hc_curr(a0),a3
           move.l      (a2),0(a3,d0.l)
           move.l      8(a2),d0
           beq.s       sigvecret               ; no flags
           not.l       d3
           and.l       d3,hc_reset(a0)

sigvecret: and.l       #NOBLOCKMASK,hc_block(a0)
           moveq.l     #0,d0
           bra         svret

sigprocmask:
           move.l      PAR3(a7),d0
           beq.s       spm_mod
           move.l      d0,a3
           move.l      hc_block(a0),(a3)
spm_mod:   move.l      PAR2(a7),d0
           beq.s       sigvecret               ; nothing to set
           move.l      d0,a2
           move.l      PAR1(a7),d0
           beq.s       spm_block
           subq.l      #1,d0
           beq.s       spm_unblock
spm_set    move.l      (a2),hc_block(a0)
           bra.s       sigpmret
spm_block  move.l      (a2),d3
           or.l        d3,hc_block(a0)
           bra.s       sigpmret
spm_unblock
           move.l      (a2),d1
           not.l       d1
           and.l       d1,hc_block(a0)         
sigpmret:  and.l       #NOBLOCKMASK,hc_block(a0)
           moveq.l     #0,d0
           bra         checksig

sigpending:
           move.l      PAR1(a7),a2
           move.l      hc_pend(a0),(a2)
           bra         sigvecret

raise:
           move.l      PAR1(a7),d2
           move.l      d2,d5
           move.l      PAR2(a7),d7

           lea         -tmp_size(a7),a7
           move.l      a7,a2
           clr.w       tmp.force(a2)
           st.b        hc_ixx(a4)
           bsr.s       sub_raise
           lea         tmp_size(a7),a7

           move.l      d0,-(a7)
           bsr         _checksig
           move.l      (a7)+,d0

           cmp.w       #-1,d0
           beq         svzret                  ; no error, just blocked
           bra         svret

* hc_ixx must be set before entering 
sub_raise:
           bsr         cseval                  ; eval handler information
           clr.b       hc_ixx(a4)
           tst.w       d0
           bmi.s       srr                     ; ?? error ?
           move.l      tmp.prio(a2),d1
           andi.l      #P_RES_R,d1
           bne.s       srni

do_raise:
           bsr         sub_chaddr              ; check handler address
           cmp.w       #-1,d0
           beq.s       sr_ok                   ; SIG_IGN
           blt.s       srha

           move.w      sr,d0
           andi.w      #SUPER,d0
           bne.s       srj                     ; must not be in sv mode
           clr.l       d0

           lea         0-esvb_len(a7),a1
           cmpa.l      tmp.stack(a2),a1
           bcs.s       srso
           move.l      a1,a7
           move.w      #sgn_raise,svb.sgn(a7)  ; frame type
           move.l      d0,svb.fs1(a7)
           move.l      d0,svb.fs2(a7)
           move.l      a1,svb.a7(a7)
           move.w      d0,svb.sr(a7)           ; just clear it
           move.l      a4,svb.a4(a7)           ; no other registers needed
           lea         sr_ok,a3                ; this is our exit point
           move.l      a3,svb.ret(a7)
           lea         cint_ret,a3
           move.l      a3,svb.finish(a7)       ; cleanup routine

           move.l      a4,esvb.hstruct(a7)     ; some xtra info
           move.l      d5,esvb.signr(a7)       ;  for handler restore
           move.l      d7,esvb.uval(a7)        ;  and reset
           move.l      d7,a4
           move.l      d5,d4

           move.l      tmp.haddr(a2),-(a7)

           jmp         ccal_setup              ; longjmp() like return
           
sr_ok:     clr.l       d0
srr:       rts

srha:      moveq       #ERR.BL,d0
           rts
srso:      moveq       #ERR.OM,d0              ; not enough stack
           rts
srni:      moveq       #ERR.NI,d0
           rts
srj:       moveq       #ERR.NJ,d0
           rts


fraise:
           move.l      PAR1(a7),d2
           move.l      d2,d5
           move.l      PAR2(a7),d7

           lea         -tmp_size(a7),a7
           move.l      a7,a2
           st          tmp.force(a2)                       ; force flag
           st.b        hc_ixx(a4)
           bsr         sub_raise
           lea         tmp_size(a7),a7
           bra         svret


* see whether some pending signals could be handled
checksig:
           pea         svzret      
* subroutine
_checksig:
           move.l      hc_block(a4),d0
           not.l       d0
           move.l      hc_pend(a4),d6          ; remember old value
           and.l       d6,d0
           beq.s       _csret                  ; nothing to do

           st.b        hc_ixx(a4)              ; inside checksig flag
**         bne.s       _icsr
           move.l      hc_nsigs(a4),d1
           moveq.l     #1,d2                   ; d2 nr to check
           move.l      d2,d3                   ; d3 mask
           move.l      d0,d4                   ; (potentialy) raiseable mask

           lea         -tmp_size(a7),a7
           move.l      a7,a2                   ; tmp.* struct
           
checkloop: st.b        hc_ixx(a4)              ; set again (in loop!)
           move.l      hc_block(a4),d0
           not.l       d0
           and.l       hc_pend(a4),d0
           and.l       d3,d0                   ; sigmask(i)
           beq.s       nxchl                   ; not this one

           move.l      d2,d5       ; prepare raise() call: signr
           clr.l       d7          ;  uval

           movem.l     d1-d4/d6/a1/a4/a5,-(a7)
           move.l      hc_uval(a4),d0
           bne.s       guval
chraise:   bsr         sub_raise
           movem.l     (a7)+,d1-d4/d6/a1/a4/a5

nxchl:     asl.l       #1,d3
           addq.l      #1,d2
           cmp.w       d2,d1
           bge.s       checkloop

           lea         tmp_size(a7),a7
_csret:    clr.b       hc_ixx(a4)
           move.l      hc_pend(a4),d0
           not.l       d6
           and.l       d6,d0
           bne.s       _checksig   ; some new signals to check
           rts

guval:     move.l      d0,a0
           move.l      d5,d0
           asl.l       #2,d0
           move.l      0(a0,d0.l),d7
           bra.s       chraise


sverr.or:  moveq       #ERR.OR,d0
           bra.s       svret
sverr.bp:  moveq       #ERR.BP,d0
           bra.s       svret
sverr.ni:  
svnoimp:   moveq       #ERR.NI,d0
           bra.s       svret
sverr.no:  moveq       #ERR.NO,d0
           bra.s       svret

svzret:    clr.l       d0
svret:     movem.l     (a7)+,d1-d7/a0-a5
           rts


****************



sysvar_base:
           dc.l        0
t_p_s:
ticks_per_second:
           dc.w        50

config:
           dc.b        '<<QCFX>>01'            ; Flag,level
           dc.w        cne-cns
cns:       dc.b        'Signal Extension'      ; Name
cne:       ds.w      0
           dc.w        4
           dc.l        version                 ; Version
* ticks per seconds
           dc.b        10                      ; Item type: word
           dc.b        0                       ; Item selection Key
           dc.w        t_p_s-*                 ; Pointer to item
           dc.w        0                       ;            pre-proc
           dc.w        0                       ;            post-proc
           dc.w        tps_text-*              ;            description
           dc.w        tps_attr-*              ;            attributes
           dc.w        -1                      ; End flag

tps_text   dc.w        tpte-tpts
tpts:      dc.b        'Ticks per second'
tpte:
           ds.w      0
tps_attr:  dc.w        49,61
           dc.w        -1                      ; End Flag

           end

