! Mouse supervisor ! (c) RWT July 1988 %option "-low-nons-half-nodiag-nocheck-nostack-nowarn" %include "mouse:nmouse.inc-nolist" @16_ff2003 %byte level 1 interrupt register %constinteger userbit=128,privbit=64 %routine rommoan(%string(255)s,%integer n) rompsym(nl); rompstr(s); rompsym(' '); romphex(n); rompsym(nl) %end *temp d0 %systempredicate valid object(%record(*)%name r) %register(a0)%record(objectfm)%name o %integer x = addr(objectpool) x = a0-x %falseunless 0<=x i = i//1 {back off a bit i = i-1 {keep trying %repeatuntil i=0 *temp rommoan("*Lock: Timeout ",addr(x)) %else rommoan("*Lock: Invalid Queue ",addr(x)) %finish {Even after error, pretend all is well} done: *move.l (sp)+,a0 %end %routine unlock(%registerrecord(queue fm)%name x) *bclr #7,x_lock %end %systemroutine setup queue (%registerrecord(queue fm)%name q) ! Make Q canonically empty. q_queue == q q_forward == q q_backward == q %end %systempredicate queue empty (%registerrecord(queue fm)%name q) ! Return TRUE/FALSE if Q is EMPTY/NOT. ! Empty queue descriptors are EXPECTED to be canonical, ! but we tolerate NIL (in which case we MAKE it canonical). %label t,s *move.l q_forward,d0; *beq s {setup queue(q) %andtrueif q_forward==nil} *cmp.l d0,q; *beq t {%trueif q_forward==q} %false s:setup queue(q) t:%true %end %systemroutine enqueue(%registerrecord(queue fm)%name c,q) ! Add to end %register(a2)%record(queue fm)%name t *exg c,q %if queue empty(c{i.e.q}) %start;%finish {force canonical form} *exg c,q *move.l t,-(sp) t == q_backward t_forward == c; q_backward == c c_forward == q; c_backward == t; c_queue == q *move.l (sp)+,t %end %routine lenqueue(%registerrecord(queuefm)%name c,q) ! Lock Q, then enqueue C on Q, then unlock Q. *exg c,q lock(c{i.e.q}) *exg c,q enqueue(c,q) *exg c,q unlock(c{i.e.q}) *exg c,q %end %systemrecord(*)%map dequeue(%record(queue fm)%name q) ! Remove from front %record(queue fm)%name c,h *temp a0 %result == nil %if queue empty(q) c == q_forward h == c_forward q_forward == h h_backward == q setup queue(c) %result == c %end %record(*)%map ldequeue(%record(queuefm)%name q) %record(queuefm)%name c *temp a0 lock(q) c == dequeue(q) unlock(q) %result == c %end %systemroutine requeue(%registerrecord(queue fm)%name c,q) ! Add to front %register(a2)%record(queue fm)%name h *exg c,q %if queue empty(c{i.e.q}) %start;%finish {force canonical form} *exg c,q *move.l h,-(sp) h == q_forward h_backward == c; q_forward == c c_backward == q; c_forward == h; c_queue == q *move.l (sp)+,h %end %systemrecord(*)%map unqueue(%record(queue fm)%name q) ! Remove from end %record(queue fm)%name c,t *temp a0 %result == nil %if queue empty(q) c == q_backward t == c_backward q_backward == t t_forward == q setup queue(c) %result == c %end *temp %systemroutine exqueue(%record(queue fm)%name c) ! Remove from middle %record(queue fm)%name l,r %unless c_queue==nil %start l == c_backward; r == c_forward l_forward == r; r_backward == l %finish setup queue(c) %end %systemroutine inqueue(%record(queue fm)%name c,l,r) ! Insert C between L and R %if l_forward==r %and r_backward==l %and l_queue==r_queue %start c_queue == l_queue c_forward == r; c_backward == l l_forward == c; r_backward == c %else setup queue(c) %finish %end %routine suspend(%register(a1{*NB*})%record(queue fm)%name q) ! Suspend the current process by queueing it on Q. ! Alert the local scheduler, then enter an idle loop. ! NB Caller must be in supervisor mode at IPL 7. lenqueue(current process_header,q) %unless q==nil current process == nil level 1 interrupt register = 255 a7 = 16_3f00-6*64 {{memtop {switch to idle interrupt stack %cycle *stop #16_2000 %repeat %end %systemroutine int signal semaphore (%registerrecord(semaphore fm)%name s) ! Callable directly by interrupt handlers, which must not use TRAPs. ! Also called from within supervisor. %register(a0)%record(queue fm)%name h0 %register(a1)%record(queue fm)%name h1 %register(a1)%record(process fm)%name p %register(a1)%record(run queue fm)%name q %unless valid object(s) %and s_header_tag='S' %start *temp rommoan("Int Signal: Invalid Semaphore",addr(s)) %return %finish *temp 0 *mfsr d1 *otsr #16_700 lock(s_header) s_counter = s_counter+1 %if s_counter>0 %start {no kicking needed} unlock(s_header) %else ! Now *very carefully* transfer the process at the head of the ! semaphore wait queue to the tail of that process's run queue. h1 == s_header {A0:S, A1:S h0 == dequeue(s_header) {A0:P, A1:S *exg h0,h1 {A0:S, A1:P unlock(h0) {unlock(s_header) h0 == p_runqueue_header {A0:Q, A1:P lock(h0) *exg h0,h1 {A0:P, A1:Q enqueue(h0,h1) {enqueue(p_header,q_header) *temp a0 unlock(q_header) {A0:Q, A1:Q q_tickler = 255 {A0:T, A1:Q %finish *mtsr d1 %end ! Supervisor interface %systemroutine move to sr(%registerinteger x) ! Loophole: Move X into SR *move.l (sp)+,a0 *trap #0 *jmp (a0) %end %systemintegerfn or to sr(%registerinteger x) ! Loophole: Or X into SR, result: previous SR *move.l (sp)+,a0 *trap #1 *jmp (a0) %end %systemintegerfn cpu time *sub.l a0,a0 *trap #2 %end %systemintegerfn elapsed time *sub.l a0,a0 *trap #2 %result = system elapsed time-d1 %end %systemintegerfn time slice size *sub.l a0,a0 *trap #2 %result = d2 %end %systemintegerfn priority *sub.l a0,a0 *trap #2 %result = d3 %end %systemintegerfn privilege *sub.l a0,a0 *trap #2 %result = d4 %end %systemroutine set time slice size(%integer ticks) *sub.l a0,a0 *trap #2 d2 = ticks *trap #3 %end %systemroutine set priority(%integer prio) *sub.l a0,a0 *trap #2 d3 = prio *trap #3 %end %systemroutine set privilege(%integer onoff) *sub.l a0,a0 *trap #2 d4 = 0 d4 = 1 %unless onoff=0 *trap #3 %end %systempredicate valid semaphore(%record(semaphorefm)%name s) %falseunless valid object(s) %and s_header_tag='S' %true %end %systempredicate valid mailbox(%record(mailbox fm)%name m) %falseunless valid object(m) %and m_header_tag='M' %falseunless valid semaphore(m_semaphore) *move.l m,a0 %true %end %routine check semaphore(%record(semaphorefm)%name s) %signal 0,4,addr(s),"Invalid Semaphore" %unless valid semaphore(s) %end %routine check mailbox(%record(mailboxfm)%name m) %signal 0,4,addr(m),"Invalid Mailbox" %unless valid mailbox(m) %end %routine check message(%record(messagefm)%name m) %returnif valid message(m) %and queue empty(m_header) %signal 0,4,addr(m),"Invalid Message" %end %systemroutine signal semaphore(%record(semaphore fm)%name s) check semaphore(s) *trap #6 %end %systemroutine semaphore wait(%record(semaphore fm)%name s) check semaphore(s) %unless s==nil *trap #7 %end %systemrecord(message fm)%map get message buffer {from pool} semaphore wait(message semaphore) %register(a0)%record(*)%name m *trap #8 %result == m %end %systemroutine put message buffer {to pool} (%record(message fm)%name m) check message(m) *trap #9 signal semaphore(message semaphore) %end %systemroutine put message {into mailbox without signalling semaphore} - (%record(message fm)%name message, %record(mailbox fm)%name mailbox) check mailbox(mailbox) check message(message) *move.l mailbox,a1 *trap #10 %end %systemroutine send message {to mailbox and signal semaphore} - (%record(message fm)%name message, %record(mailbox fm)%name mailbox) put message buffer(message) %andreturnif mailbox==nil put message(message,mailbox) signal semaphore(mailbox_semaphore) %end %systemrecord(message fm)%map get message {out of mailbox without waiting} - (%record(mailbox fm)%name mailbox) check mailbox(mailbox) *trap #11 %end %systemrecord(message fm)%map receive message {out of mailbox after waiting} - (%record(mailbox fm)%name mailbox) semaphore wait(mailbox_semaphore) %result == get message(mailbox) %end %record(*)%map create kernel object(%string(255)id, %register(a1)%integer tag,param) %string(31)dump *movem.l d0-d7,dump *movem.l id,d0-d7 *trap #12 *movem.l dump,d0-d7 %end %record(*)%map lookup kernel object(%string(255)id,%register(a1)%integer tag) %string(31)dump *movem.l d0-d7,dump *movem.l id,d0-d7 *trap #13 *movem.l dump,d0-d7 %end %systemrecord(semaphore fm)%map create semaphore(%string(255)id,%integer c) %record(*)%name s == create kernel object(id,'S',c) %result == s %unless s==nil id = "Cannot create semaphore ".id %signal 0,4,,id %end %systemrecord(semaphore fm)%map lookup semaphore(%string(255)id) %record(*)%name s == lookup kernel object(id,'S') %result == s %unless s==nil id = "Cannot find semaphore ".id %signal 0,4,,id %end %systemroutine delete semaphore(%record(semaphore fm)%name s) check semaphore(s) *trap #14 %end %systemrecord(mailbox fm)%map create mailbox - (%string(255)id,%record(semaphore fm)%name s) %record(*)%name m == create kernel object(id,'M',addr(s)) %result == m %unless m==nil id = "Cannot create mailbox ".id %signal 0,4,,id %end %systemrecord(mailbox fm)%map lookup mailbox(%string(255)id) %record(*)%name m == lookup kernel object(id,'M') %result == m %unless m==nil id = "Cannot find mailbox ".id %signal 0,4,,id %end %systemroutine delete mailbox(%record(mailbox fm)%name m) check mailbox(m) *trap #14 %end ! Interrupt handling %register(a4)%record(interrupt handler fm)%name current interrupt handler %systemroutine return from interrupt @0(a7)%integer pc current interrupt handler == current interrupt handler_forward pc = current interrupt handler_pc %end %systemroutine wait for interrupt @0(a7)%integer pc current interrupt handler_pc = pc current interrupt handler == current interrupt handler_forward pc = current interrupt handler_pc %end %systemroutine add interrupt handler(%integer level) %register(a1)%record(queuefm)%name q %register(d3)%integer sr %returnunless 1<=level<=7 q == int chain(level)_header sr = ortosr(16_2700) enqueue(current interrupt handler_header,q) movetosr(sr) *nop {let's not fall foul of the over-eager optimiser - aargh} %end *temp d0-d1/a0-a1 ! Implementation of TRAPs ! Although written as routines, none of these actually ! reach their %end, as they have to return using *RTE. %routine trap0 {move to sr} (%registerinteger v) @0(a7)%short sr *swap v; *move.w sr,v *swap v; *move.w v,sr *swap v; *ext v *rte %end %routine trap1 {or to sr} (%registerinteger v) @0(a7)%short sr *swap v; *move.w sr,v *swap v; *or.w v,sr *swap v; *ext v *rte %end %routine trap2 (%registerrecord(process fm)%name process) ! Obtain process attributes process == current process %if process==nil d3 = process_priority&7 d4 = 1; d4 = 0 %if process_asn&privbit=0 d2 = process_time slice size d1 = system elapsed time-process_start time d0 = process_cputime *rte %end %routine trap3 (%registerrecord(process fm)%name process, %register(d2)%integer time slice size,priority,privilege) ! Change process attributes process == current process %if process==nil *otsr #16_700 %if privilege&1=0 %start process_asn = process_asn&\privbit %else process_asn = process_asn!privbit %finish priority = priority&7 process_runqueue == runqueue(priority) process_priority = priority process_time slice size = time slice size process_time slice left = time slice size *movem.l d0-d1/a0-a1/a4,-(sp) *mfusp a4 *movem.l d2-d7/a2-a6,-(sp) current process_ssp = a7 suspend(current process_runqueue_header) %end %routine spare trap %integer x=integer(16_1010) @6(a7) %integer pc pc = pc-2 *rts %end %routine trap6 {signal semaphore} (%registerrecord(semaphore fm)%name s) int signal semaphore(s) *rte %end %routine trap7 {semaphore wait} (%registerrecord(semaphore fm)%name s) %register(a0)%record(queue fm)%name h0,h1 *temp d0/a1 *otsr #16_700 suspend(nil) %if s==nil {quiet death} lock(s_header) s_counter = s_counter-1 %if s_counter<0 %start {need to wait} s_counter = s_counter+1 {leave consistent while unlock(s_header) {we preserve context} *movem.l d0-d1/a0-a1/a4,-(sp) *mfusp a4 *movem.l d2-d7/a2-a6,-(sp) currentprocess_ssp = a7 lock(s_header) s_counter = s_counter-1 {do again} %if s_counter<0 %start {it should be} *temp 0 h1 == s_header h0 == currentprocess_header enqueue(h0,h1) *temp a0/a1 unlock(h1) suspend(nil) %finish unlock(s_header) {otherwise false alarm} *movem.l (sp)+,d2-d7/a2-a6 {undo preservation} *movem.l (sp)+,d0-d1/a0-a1/a4 *rte %finish unlock(s_header) *rte %end %routine trap8 {get message buffer} %register(a0)%record(message fm)%name message %integer i *otsr #16_700 %for i = 1,1,message pool size %cycle message == message pool(next message) next message = next message-1 next message = message pool size-1 %if next message<0 %if message_header_owner==nil %start message_header_owner == current process *add.l #4,sp; *rte %finish %repeat message == nil *add.l #4,sp; *rte %end %routine trap9 {put message buffer} (%registerrecord(message fm)%name message) message_header_owner == nil *rte %end %routine trap10 {put message} (%registerrecord(message fm)%name message, %registerrecord(mailbox fm)%name mailbox) *otsr #16_700 lenqueue(message_header,mailbox_header) *rte %end %routine trap11 {get message} (%record(mailbox fm)%name mailbox) %record(message fm)%name message *otsr #16_700 message == ldequeue(mailbox_header) *move.l message,a0 *lea 8(sp),sp *rte %end *temp %routine trap12 {create object} (%register(a1)%integer tag,param) ! NB object name passed in d0-d7 %string(31)id %record(object fm)%name o %integer i *movem.l d0-d7,id *clr.l d4 i = 0 length(id) = 31 %if length(id)>31 i = objectpoolsize %if id="" %cycle %if i=objectpoolsize %start {not found} %for i = 1,1,object pool size %cycle o == object pool(next object) next object = next object-1 next object = object pool size-1 %if next object<0 %if o_header_owner==nil %start o_header_owner == current process o_name = id o_param = param o_header_tag = tag setup queue(o_header) *move.l o,a0 *lea 40(sp),sp *rte %finish %repeat o == nil; %exit {no more objects available} %finish o == object pool(i) %if o_header_owner##nil %and o_name=id %and o_header_tag=tag %start %exit {already there} %finish i = i+1 %repeat *move.l o,a0 *lea 40(sp),sp *rte %end %routine trap13 {lookup object} (%register(a1)%integer tag) ! NB object name passed in d0-d7 %string(31)id %integer i %record(objectfm)%name o *movem.l d0-d7,id *clr.l d4 i = 0 length(id) = 31 %if length(id)>31 i = objectpoolsize %if id="" %cycle o == nil %andexitif i=objectpoolsize o == object pool(i) %exitif o_header_owner##nil %and o_header_tag=tag %and o_name=id {found} i = i+1 %repeat *move.l o,a0 *lea 40(sp),sp; *rte %end *temp d0-d1/a0-a1 %routine trap14 {delete object} (%registerrecord(object fm)%name object) object_header_owner == nil %if object_header_owner==currentprocess *rte %end %routine int1 *movem.l d0-d1/a0-a1/a4,-(sp) current interrupt handler == intchain(1)_forward a0 = current interrupt handler_pc *jmp (a0) %end %routine int2 *movem.l d0-d1/a0-a1/a4,-(sp) current interrupt handler == intchain(2)_forward a0 = current interrupt handler_pc *jmp (a0) %end %routine int3 *movem.l d0-d1/a0-a1/a4,-(sp) current interrupt handler == intchain(3)_forward a0 = current interrupt handler_pc *jmp (a0) %end %routine int4 *movem.l d0-d1/a0-a1/a4,-(sp) current interrupt handler == intchain(4)_forward a0 = current interrupt handler_pc *jmp (a0) %end %routine int5 *movem.l d0-d1/a0-a1/a4,-(sp) current interrupt handler == intchain(5)_forward a0 = current interrupt handler_pc *jmp (a0) %end %routine int6 *movem.l d0-d1/a0-a1/a4,-(sp) current interrupt handler == intchain(6)_forward a0 = current interrupt handler_pc *jmp (a0) %end %routine int7 *movem.l d0-d1/a0-a1/a4,-(sp) current interrupt handler == intchain(7)_forward a0 = current interrupt handler_pc *jmp (a0) %end %routine scheduling interrupt handler @16_100002 %byte mmu ud asn,*,mmu up asn %register(d1)%integer i *otsr #16_700 interrupted priority = -1 %unless currentprocess==nil %start interrupted priority = current process_priority %if current process_time slice left<0 %start currentprocess_timesliceleft = currentprocess_time slice size *mfusp a4 *movem.l d2-d7/a2-a6,-(sp) current process_ssp = a7 lenqueue(currentprocess_header,currentprocess_runqueue_header) currentprocess == nil interrupted priority = -1 %finish %finish i = 7 %while i > interrupted priority %cycle rivalprocess == ldequeue(run queue(i)_header) %unless rivalprocess==nil %start %unless current process==nil %start *mfusp a4 *movem.l d2-d7/a2-a6,-(sp) current process_ssp = a7 lenqueue(current process_header,current process_run queue_header) %finish currentprocess == rivalprocess currentprocess_starttime = system elapsed time a7 = currentprocess_ssp *movem.l (sp)+,d2-d7/a2-a6 *mtusp a4 i = currentprocess_vbr *=16_4e7b; *=16_1801 {*movec i,vbr i = currentprocess_asn mmu ud asn = i; mmu up asn = i *movem.l (sp)+,d0-d1/a0-a1/a4 *rte %finish i = i-1 %repeat *movem.l (sp)+,d0-d1/a0-a1/a4 *rte %end {scheduling interrupt handler} *temp %begin %integer i %label intret @16_1000 %integerarray exception vector(0:47) {--------------------------} { Main program begins HERE } {--------------------------} ! Plug in exception vector table entries, ! first traps 14:0, then interrupts 7:1. *move.l a7,a0 *lea exception vector(47),a7 *pea trap14 *pea trap13 *pea trap12 *pea trap11 *pea trap10 *pea trap9 *pea trap8 *pea trap7 *pea trap6 *pea sparetrap *pea sparetrap *pea trap3 *pea trap2 *pea trap1 *pea trap0 *pea int7 *pea int6 *pea int5 *pea int4 *pea int3 *pea int2 *pea int1 *move.l a0,a7 ! Initialise pools of message buffers and kernel objects message pool = 0; next message = message pool size-1 object pool = 0; next object = object pool size-1 message semaphore == create semaphore("",message pool size) ! Initialise the interrupt handler chains %for i = 1,1,7 %cycle setup queue(intchain(i)_header); intchain(i)_pc = addr(intret) %repeat *lea scheduling interrupt handler,a0; intchain(1)_pc = a0 ! Create the run queues %for i = 0,1,7 %cycle runqueue(i) == create kernel object("",'Q',addr(level 1 interrupt register)) %repeat ! Finally a bit of sly magic to interact with loader set priority(7) record(memtop-1024) = currentprocess %return intret: *movem.l (sp)+,d0-d1/a0-a1/a4 *rte %end {setup supervisor}