home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-06-13 | 72.1 KB | 1,171 lines |
-
- *MCX.AS
- *******************************************************************************
- * *
- * MCX11 REAL-TIME KERNEL *
- * FOR THE MC68HC11 *
- * *
- * by for support contact *
- * Tom Barrett Mike Wood M/D OE319 *
- * A.T. Barrett & Associates Motorola,Inc *
- * Houston, Texas 6501 William Cannon Dr West *
- * Austin, Texas 78735 *
- * (713)728-9688 (512)891-2717 *
- * *
- *******************************************************************************
- * N O T I C E *
- *******************************************************************************
- * *
- * This product is distributed without charge to users via the MOTOROLA *
- * FREEWARE Bulletin Board. The product is provided "as is" without warranty *
- * of any kind, either expressed or implied, including, but not limited to any *
- * warranties of merchantability and fitness for a particular purpose. All *
- * risks of using this product including the entire costs of any necesary rem- *
- * edies are those of the user and MOTOROLA assumes no liability of any kind. *
- * *
- *******************************************************************************
- *******************************************************************************
- * R E V I S I O N H I S T O R Y
- * Release 1
- *
- * Rev # Date Modifications/Corrections for new Revision
- * ----- --------- ------------------------------------------------------
- * 1.1 5/30/89 1. Change calling sequence of .deque. so that the return
- * value is in ACCA if entry size is 1 byte.
- *
- * 2. Changed calling sequence of .enque. so that if the size
- * of the entry is 1 byte, it is passed to .enque. in bits
- * 8-15 of IX. If the size of the entry is 2 bytes, IX8-15
- * contains byte 1 while IX0-7 contains byte 2.
- *
- * 11/1/89 3. Corrected incorrect jump destination label in .timer.
- * from .pend to dopend
- *
- * 4. Corrected incorrect branch in .send. causing messages to
- * be put into a receiver's mailbox in improper order.
- *
- * 5. Cleared intlvl during initialization.
- *
- * 1.2 1/12/90 1. Changed the exit logic in .recv. when there is no msg
- * waiting in the mailbox. This corrects a problem which
- * would leave the task semaphore of the receiver task in
- * a WAIT state. Since the .send. ESR does not signal a
- * semaphore but simply unblocks the receiving task, it is
- * not necessary to manipulate the task's semaphore. The
- * original code is deleted and replaced with a simple
- * set which backs up the PC and exits.
- *
- * 2. Moved the code for the backup PC routine and made it an
- * internal part of the .deque. function
- *
- * 1.3 3/23/90 1. Disabled interrupts for the endfast routine in isrrtn
- * which protects this critical path from being interrupted
- * after intlvl is decremented. If this were to happen
- * multiple ISRs could try to restore a task's state.
- *
- * 2. Removed a hanging push in .signal. that was not poped
- * before returning if the semaphore was not in the wait
- * state. This would only be a problem if an ISR were
- * returning to the Dispatcher or another ISR.
- *
- * 3. Moved the sei and cli in .signal. to tighten up the
- * critical code and improve interrupt response time.
- *
- * 4. Moved the sei in .wait. to protect the Critical Code
- * there, and deleted the cli since immrtn is CC also.
- * This corrects several problems with tasks that wait on
- * semaphores from interrupts.
- * NOTE: to shorten interrupt latency here you might
- * move the cli to after setting the wait and then jump
- * to endtsk, the tradeoff is another stacked interrupt.
- *
- * 5. Changed send and receive to use the _RCVWAT status bit
- * as was origenally intended instead of _WAIT. This
- * Corrects the problem where sending a message to a task
- * that is waiting on any semaphore causes it to wake up
- * as if that semaphore had been signaled.
- *
- * 6. Added an sei to .deque. to protect some critical code
- * in .wait. (see 3 above)
- *
- * 7. In .enque. moved the loading of IY with queue data to
- * later in the routine closer to where it is actualy used.
- * This fixes the problem where the backup routine would
- * subtract 2 from a word other than the tasks PC which
- * was stored on its stack.
- *
- * 8. Minor things realy, shaved off a clock cycle in dispch's
- * tight loop, and used the _PEND symbol in .pend.
- *
- * 1.4 12/7/90 1. Changed cmpb to cmpa in task 5 of TEST.AS. Dequeue was
- * changed to return the value in the A accumulator in V1.1.
- *
- * 2. Fixed a problem with nested interrupt. If you had a
- * nested interrupt that did not require a context switch
- * followed by an interrupt that did require a context
- * switch the stack became corrupted and the system would
- * crash. Eliminated endfast routine.
- *
- * 3. Fixed a problem with purging the timers. Once a timer
- * was purged the next timer in the link was not lengthed
- * the amount of time of the purged timer.
- *
- * 4. Fixed a problem with cyclic timers in CLKDRIVER.AS.
- *
- * 1.5 1/24/91 1. Fixed a problem that I introduced in 1.4 in purge of
- * timer. Messed with y index register and didn't restore.
- *******************************************************************************
- *******************************************************************************
-
- * OPT nol Remove asterisk if you don't want the listing
-
- *******************************************************************************
- * *
- * MCX-11 VARIABLES *
- * (These Will Reside in RAM) *
- * *
- *******************************************************************************
-
- ************************ Filled in by Initialization **************************
- tickcnt equ MCXVAR Tick counter
- FREE equ tickcnt+1 Address of first free timer block
- ACTIVE equ FREE+2 Address of first active timer in list
- *******************************************************************************
-
- curtsk equ ACTIVE+2 Current task (i.e. the active task)
- curtcb equ curtsk+1 Address of current task's TCB
- hipri equ curtcb+2 Highest priority task ready to run
- pritcb equ hipri+1 Address of TCB of highest priority task
- * ready to run (see hipri)
- intlvl equ pritcb+2 Depth of nested interrupts:
- * = 0 when in a task
- * = >0 when interrupts are nested or
- * in the kernel
- temp equ intlvl+1 Temporary area
- width equ temp+2 Work area for queue width
- depth equ width+1 Work area for queue depth
- notmt equ depth+1 Work area for queue not empty semaphore
-
- *******************************************************************************
- * TASK STATE EQUATES *
- *******************************************************************************
-
- _SUSPND equ $80 SUSPENDed status
- _WAIT equ $40 WAITing status
- _RCVWAT equ $20 Waiting status for message RECEIVE
- _IDLE equ $01 Task IDLE status
-
- *******************************************************************************
- * MESSAGE EQUATES *
- *******************************************************************************
-
- MLINK equ 0 Message link pointer
- MTASK equ 2 Message's sending task
- MSEMA equ 3 Message semaphore
- MBODY equ 4 Start of message body
-
- *******************************************************************************
- * TIMER BLOCK EQUATES *
- *******************************************************************************
-
- CLINK equ 0 Timer block link pointer
- CTOCKS equ 2 Clock tocks in timer
- CRESET equ 4 Reset timer
- CTASK equ 6 Task waiting on timer
- CSEMA equ 7 Semaphore number
-
- *******************************************************************************
- * QUEUE HEADER EQUATES *
- *******************************************************************************
-
- CURSIZ equ 0 Current size of queue (# of entries)
- PIX equ 1 Put Index
- QSEMA equ 2 Active semaphore: NOTMT or NOTFUL (=NOTMT+1)
-
- *******************************************************************************
- * TCB LAYOUT *
- *******************************************************************************
- *
- STATE equ 0 Byte 0 Task status:
- * bit 7: Suspended
- * bit 6: Waiting for an event
- * bit 5: Receive wait
- * bit 4: - Reserved -
- * bit 3: - Reserved -
- * bit 2: - Reserved -
- * bit 1: - Reserved -
- * bit 0: Task not in use
- ACTSP equ 1 Byte 1-2 Active Stack Pointer for task
- MSGTHRD equ 3 Byte 3-4 Message thread pointer
-
- *******************************************************************************
- * STACK CONTEXT EQUATES *
- * (These values represent offsets from the Top-of-Stack +1) *
- *******************************************************************************
-
- CCR equ 0 Condition Code Register
- ACCB equ 1 Accumulator B
- ACCA equ 2 Accumulator A
- IX equ 3 Index Register X
- IXH equ 3 Index Register X (High Byte)
- IXL equ 4 Index Register X (Low Byte)
- IY equ 5 Index Register Y
- PC equ 7 Program Counter
-
- *******************************************************************************
- * MISCELLANEOUS EQUATES *
- *******************************************************************************
-
- _PEND equ 1 PENDing state
-
- PAGE
- *******************************************************************************
- * *
- * MCX11 TASK DISPATCHER *
- * *
- *******************************************************************************
- * *
- * This is the code that looks for the highest priority task that is in a RUN *
- * state. The tsk's state is contained in the Task Control Block (TCB). The *
- * job of the Dispatcher is to find the task having the highest priority and *
- * is ready to take control, i.e., run. This is a tight loop because every *
- * time a task gives up control, except for a pre-emption, the Dispatcher is *
- * called to find the next task to run. *
- * *
- *******************************************************************************
-
- dispch clra Use acc A for the task number
- ldx #STATLS-TCBLEN Set up base address of TCB list
- next clr curtsk Set current task # to 0
- lds #SYSTACK Load the system stack pointer address
- ldab #$7F Load constant 127 for presetting
- stab hipri the task # of the highest priority task
- * - 1 is the highest priority
- * - 127 is the lowest priority
- ldab #TCBLEN Load a constant for TCB length attribute
- cli Enable interrupts
- testat inca Bump the task number by 1
- abx Get address of next TCB
- *** (V1.3 #8) ***
- tst STATE,x See if all bits in task's status are 0
- bne testat If not, go check the next task.
- switch sei Task is ready to run. disable interrupts
- staa hipri Save task number as highest priority
- stx pritcb Save TCB address as that of hipri task
- setcur staa curtsk also save acc A as current task number
- stx curtcb Save X-reg as address of curtsk's TCB
- rtncur lds ACTSP,x Load task's stack pointer from TCB
- intrtn rti Go to task via an interrupt return. The
- * task's context is on the task's stack.
- * This treats the context switch as if it
- * is an interrupt (it is likely to be).
-
- *******************************************************************************
- * *
- * MCX11 COMMON INTERRUPT SERVICE RETURN *
- * *
- *******************************************************************************
- * *
- * This is the code that ALL interrupt service routines (ISR) MUST return thru *
- * to complete the action of the interrupt. The ISR must have saved the MPU *
- * context, incremented the nested interrupt level (intlvl), and enabled other *
- * interrupts before actually servicing the interrupt. Upon completion, the *
- * ISR, should branch to this common interrupt return routine. The B accumu- *
- * lator should contain a semaphore number or a zero. The former value *
- * indicates that an event has occurred that requires action at the task level *
- * and the latter indicates thee is no further action required. *
- * *
- * When ACCB contains a semaphore number, the semaphore is SIGNALed to inform *
- * any task associated with the event that it has occurred. A context switch *
- * will occur if the task in a WAIT state for that event is then ready to run *
- * and is of higher priority than the interrupted,i.e. current, task, and the *
- * level of nested interrupts is 1. A context switch will not occur if ALL of *
- * these conditions are not true. If the B accumulator contains a zero upon *
- * return from the ISR, there is an immediate return to the interrupted task *
- * if the level of nested interrupts is 1 or a return to the executive or to *
- * an interrupted ISR if the level of nested interrupts is >1. *
- * *
- *******************************************************************************
- *
- isrrtn tstb Test ACCB for content
- bne signal2 If ACCB != 0, go signal the event.
- * If ACCB = 0, return to point of interrupt
- *
- *** (V1.4 #2) ***
- bra endtsk Eliminated endfast routine. |
-
- .signal tab *** PUT IN COMMENT BLOCK ***
- signal2
- ldx #FLGTBL-1 Got a semaphore number. Load address-1 of
- abx semaphore table and add semaphore # to it
- clrb Clear ACCB so that we will have a zero
- sei Turn off interrupts *** (V1.3 #2) ***
- ldaa 0,x Get the content of the semaphore
- bpl nowait Branch if semaphore not in WAIT state
- incb If WAITing, set it to PENDing
- nowait stab 0,x If PENDing or DONE, set to DONE
- tab Look at original content of semaphore
- *** (V1.4 #2) ***
- bpl immrtn Return immediately if semaphore not WAITing|
- cli End of critical code *** (V1.3 #2) ***
- negb If WAITing, 2's complement = task # WAITing
- * for the event.
- *** (V1.3 #3) ***
- ldaa #-_WAIT-1 Store the 1's complement of the WAIT
- clrstat pshb Save the task number.
- psha Then save the mask again.
- ldaa #TCBLEN Use the task number to compute the address
- mul of its TCB
- addd #STATLS-TCBLEN
- xgdx Put the TCB address in IX register
- pula Get the mask of bits to clear.
- clrbits sei
- anda 0,x Clear the byte as given by the mask.
- staa 0,x Save the updated TCB status byte.
- pula Get the task number again.
- bne immrtn Branch to immediate exit if task's status
- * indicates it is not ready to run (!=0).
- cmpa hipri If it is ready to run, is it of higher
- * priority than the current highest
- * priority task which is ready to run?
- bge immrtn Branch if task priority < hipri.
- sta hipri If so, then make it the new hipri task.
- stx pritcb Save its TCB address too.
-
- endtsk sei Disable interrupts
- immrtn dec intlvl Decrement the interrupt level.
- bne intrtn If it is >0, then the interrupt occurred
- * while we were doing system operations.
- ldaa curtsk See if the current task is 0.
- bne notdisp If not, branch.
- tsx If so, the MCX11 Dispatcher was interrupted
- * See what task was being checked when the
- * interrupt occurred. The context of the
- * Dispatcher is found on the system stack.
- ldaa ACCA,x Get the content of ACCA from the stack as
- * it holds the value of the "current task"
- * being used by the Dispatcher.
- cmpa hipri Compare it to hipri task number.
- ble intrtn The "current task" is of higher priority.
- notdisp ldx pritcb Get the hipri task's TCB address
- ldaa hipri and set up the hipri task number.
- brclr STATE,x $FF setcur If task is ready to run, go to it
- bra next Otherwise, look for next lower priority
- * task that is ready to run.
-
- *******************************************************************************
- * *
- * MCX11 EXECUTIVE SERVICES DISPATCHER *
- * *
- *******************************************************************************
- * *
- * The Executive Services Dispatcher is the heart of MCX11. This is the point *
- * to which all Executive Service Requests (ESR) are vectored. The ESR Dis- *
- * patcher takes the function code of the ESR requested by the task and uses *
- * it as an index into a jump table to the various ESR functions. This code is *
- * entered by a SWI instruction so it must be treated as a special kind of *
- * interrupt. The nested interrupt level is always =0 upon entry because the *
- * ESR is coming from a task. The ESR function is always found at the byte *
- * immediately following the SWI. *
- * *
- *******************************************************************************
-
- mcxsrv inc intlvl Increment the interrupt nest level
- ldx curtcb
- sts ACTSP,x Save SP in TCB of current task
- tsy Save Top-of-Stack (TOS) in IY. This will
- * actually point to the CCR byte
- lds #SYSTACK Load SP with address of system stack
- cli Now interrupts are turned on
- ldx PC,y Set up pointer to the ESR function
- ldab 0,x Get the ESR function code
- inx Bump the return address by 1
- stx PC,y Save the return address
- aslb Multiply the ESR function code by 2
- ldx #jtable Load address of the ESR jump table
- abx Add function code*2 to base address of
- * jump table to get ESR vector
- ldx 0,x Load the ESR vector into IX
- ldd ACCB,y Load up ACCA and ACCB with the arguments
- * as passed by the calling task but
- * switch ACCA and ACCB contents.
-
- *******************************************************************************
- * *
- * At this point the processor context is as follows: *
- * *
- * CCR conditions wrt contents of ACCB & ACCA (don't use) *
- * ACCA ACCB as passed from calling task (ESR dependent) *
- * ACCB ACCA as passed from calling task (ESR dependent) *
- * IX Address of the ESR function *
- * IY Address of task's Top-of-Stack (CCR byte) *
- * SP Base of System Stack *
- * *
- * In addition, the nested interrupt level is = 1, curtsk and hipri are set *
- * to the number of the calling task, and curtcb and hipri both contain the *
- * address of the current task's TCB. *
- * *
- *******************************************************************************
-
- jmp 0,x Go to the ESR function
-
- *******************************************************************************
- * *
- * ESR JUMP TABLE *
- * *
- *******************************************************************************
-
- jtable FDB endtsk 0 = Immediate return (NOP)
- FDB .wait 1 = Wait for an event
- FDB .signal 2 = Signal a semaphore
- FDB .pend 3 = Set a semaphore to PENDing state
- FDB .send 4 = Send a task a message without waiting
- FDB .sendw 5 = Send a task a message with wait
- FDB .recv 6 = Receive a message
- FDB .deque 7 = Dequeue an entry from a FIFO queue
- FDB .enque 8 = Enqueue an entry into a FIFO queue
- FDB .resume 9 = Resume a task in suspension
- FDB .suspnd 10 = Suspend a task's operation
- FDB .termn8 11 = Terminate a task's operaton
- FDB .xeqt 12 = Execute a task
- FDB .delay 13 = Delay a task for a period of time
- FDB .timer 14 = Set up a timer
- FDB .purge 15 = Remove a given timer from active state
-
-
- * ESR1
- *******************************************************************************
- * *
- * WAIT for an event to occur. This ESR is used to place the calling task into *
- * a WAIT state where it will remain until the event happens. If the event has *
- * already occurred, the wait does not happen and control is immediately re- *
- * turned to the calling task. WAIT is always associated with a semaphore. The *
- * semaphore can be explicitly stated or it can be implicit, i.e. the task's *
- * semaphore. Regardless of how the semaphore is defined in the ESR, the sema- *
- * phore must be in the PENDing state if the WAIT is to occur. The process *
- * handling the signaling of the event when it occurs must have prior know- *
- * ledge that the event uses a particular semaphore. Only in this way can the *
- * waiting task and the signalling task be connected. *
- * *
- * calling sequence: *
- * *
- * ACCB = semaphore number *
- * swi *
- * FCB .wait. *
- * return: all registers unchanged *
- * *
- *******************************************************************************
-
- .wait tab Test the semaphore number
- bne wait2 If it is defined, go set up the wait
- ldab curtsk Otherwise use the task's semaphore
- addb #NNAMSEM
- wait2 ldx #FLGTBL-1
- abx Compute address of the semaphore
- sei Start of critical code *** (V1.3 #4) ***
- tst 0,x Test the semaphore contents
- bmi waitt Branch if already in the WAIT state
- * This is a safety net, if you should unin-
- * tentionally have more than one task wait-
- * ing on a semaphonre at a time all but the
- * first will not wake up unless they are re-
- * exicuted.
- beq pendset If DONE, go reset to PENDing and return
- force ldaa curtsk If state of semaphore is PENDing, the
- nega event has not yet occurred so
- staa 0,x Set the semaphore to WAIT on current task.
- waitt ldx curtcb Get current task's TCB address
- bset STATE,x _WAIT Set the WAIT state in task's status byte.
- *** (V1.3 #4) ***
- jmp immrtn Critical code ends at actual return to task
-
- *******************************************************************************
- * *
- * Force a semaphore to a PENDing state. This function should be used to make *
- * sure that a semaphore is in the correct state if there is some doubt. A *
- * semaphore must be in a PENDing state before it can transition to a WAITing *
- * state. All semaphores should be initialized to a PENDing state at the time *
- * of system initialization. After that, they are generally maintained by the *
- * .wait and .signal functions. However, it may become necessary at some time *
- * to set a given semaphore PENDing to insure that the event is recognized as *
- * having yet occurred. *
- * *
- * calling sequence: *
- * *
- * ACCB = semaphore number *
- * swi *
- * FCB .pend. *
- * return: all registers unchanged *
- * *
- *******************************************************************************
-
- .pend tab Put semaphore number in ACCB
- bne dopend Branch if semaphore is defined.
- ldab curtsk Else use task semaphore.
- addb #NNAMSEM
- dopend ldx #FLGTBL-1 Compute the address of the given semaphore.
- abx
- pendset ldaa #_PEND Set the semaphore to pending
- sei
- staa 0,x
- jmp immrtn All done.
-
- *******************************************************************************
- * *
- * Send a message and wait for reply. This function is the same as the .send. *
- * function except that it puts the sender into a Wait state until the task *
- * receiving the message signals the semaphore to indicate it is finished. *
- * *
- * calling sequence: *
- * *
- * ACCA = Task number of the receiver *
- * ACCB = Semaphore number *
- * IX = Message address *
- * swi *
- * FCB .sendw. *
- * return: Registers are unchanged *
- * ACCB will = Current task # if the semaphore *
- * number was = 0 at the time of the ESR *
- * *
- *******************************************************************************
-
- .sendw clrb Set switch = 0 for .sendw.
-
- *******************************************************************************
- * *
- * Send a message. This function sends a message but does not wait for a reply *
- * to be returned from the receiver. There is a semaphore associated with the *
- * message but it is set to a PENDing state. The Sending task may perform a *
- * .wait. on that semaphore at a later time if necessary. Neither .send. nor *
- * .sendw. moves data from the sender to the receiver. Instead, only the *
- * pointer to the message is moved. Each task has a threaded list of messages *
- * which it can receive. The threaded list entry is the pointer to the message.*
- * The message pointer is inserted into the threaded list in the order of the *
- * sending task's priority. *
- * *
- * calling sequence: *
- * *
- * ACCA = Task number of the receiver *
- * ACCB = Semaphore number *
- * IX = Message address *
- * swi *
- * FCB .send. *
- * return: Registers are unchanged *
- * ACCB will = Task semaphore # if ACCB was = 0 *
- * at the time of the ESR. *
- * *
- *******************************************************************************
-
- .send pshb Save the switch. Switch > 0 for .send
- ldab ACCB,y Get the semaphore number for the message.
- bne havsema Branch if semaphore # > 0.
- ldab curtsk If semaphore # = 0, use task semaphore.
- addb #NNAMSEM
- stab ACCB,y
- havsema ldx #FLGTBL-1
- abx Compute address of the semaphore.
- pulb Get the send switch.
- tstb Test switch.
- bne notw Branch if switch set for no waiting.
- ldaa curtsk
- nega If switch set for wait, set -task #.
- notw staa 0,x Set the semaphore to proper value.
- ldx curtcb Get address of current task's TCB.
- tstb Test the switch again.
- bne waitnot
- bset STATE,x _WAIT Set the sender's status to WAITing.
- waitnot ldaa ACCA,y Get the receiving task #.
- psha Save it for later use, too.
- ldab #TCBLEN
- mul Compute TCB address of receiving task.
- addd #STATLS-TCBLEN
- xgdx
- pshx Save TCB address for later use.
- ldab #MSGTHRD
- abx
- ldaa ACCB,y Get the semaphore number
- ldy IX,y Load IY with message address.
- staa MSEMA,y Set up semaphore # in message header.
- ldaa curtsk
- staa MTASK,y Set up current task as sender.
- pshy Save address of the message header.
- nsrtlp ldy MLINK,x Look at the receiver's next message address.
- beq insert If pointer = 0, End-of=List. Go insert here.
- cmpa MTASK,y If there is a message on the thread, look at
- * the task number of its sender and compare
- * it to the sender's task number for this
- * new message.
- *
- blo insert If the new message's sender has a priority |
- * higher than that of the threaded message,|
- * go insert the new message pointer in front
- * of the one on the list. (V1.1 #4) |
- ldx MLINK,x Otherwise, walk the thread.
- bra nsrtlp Keep looking for a place to insert new msg.
- insert xgdy Put address of next nessage in ACCD.
- puly Get address of new message's header.
- std MLINK,y Link new message into threrad.
- xgdy
- std MLINK,x
- pulx Get address of TCB of receiver again.
- ***(V1.3 #5)***
- ldaa #-_RCVWAT-1
- jmp clrbits Go clear the receiver wait, if any.
-
- *******************************************************************************
- * *
- * Receive a message. This function is used by a receiving task to get the *
- * next message from the task's message thread. Normally, the next message is *
- * pointed to by the content of the message thread pointer in the task's TCB. *
- * However, it is also possible to get the next message sent from a given task *
- * to the receiver task. By this technique, the receiver may effectively ig- *
- * nore other senders and reserve all activity for the one sending task. This *
- * is useful in designing server tasks which need to insure the dedicated use *
- * of a given resource. *
- * *
- * calling sequence: *
- * *
- * ACCA = task number (Otherwise this must be 0) *
- * swi *
- * FCB .recv. *
- * return: IX contains the address of the received message *
- * All other registers unchanged *
- * *
- *******************************************************************************
-
- .recv ldx curtcb Set up to see if there is a special task
- tstb ACCB holds the task number or 0.
- bne special If ACCB != 0, it contains the task number.
- ldd MSGTHRD,x If not given task #, get next message.
- beq waitrcv See if message thread is null. (=0)
- std IX,y It's not. We have a message. Save address.
- xgdy
- ldd 0,y Then unlink the message from the thread.
- std MSGTHRD,x
- jmp endtsk Wrap it up.
- *** (V1.3 #5) ***
- waitrcv bset STATE,x _RCVWAT If no message available, set up a RCVWAT
- *** (V1.2 #1) ***
- ldd PC,y Then back up the PC in the stacked |
- subd #2 context by 2 bytes |
- std PC,y |
- jmp endtsk Go to wrap up routine |
-
- special pshy Save address of the task's stack context.
- ldab #MSGTHRD ACCB still contains the task number.
- abx Adjust IX to point to message thread pntr.
- ldab ACCA,y Get the task number again.
- rcvloop ldy 0,x Get the address of the next message header.
- beq waitrcv If no message available, go wait for one.
- cmpb MTASK,y Compare the sender's task number to special
- beq rcvgo task number. If a match, go to it.
- blt waitrcv If special task number < sender's, wait.
- ldx 0,x If special task number > special, set up IX
- bra rcvloop to point to current message header and
- * then go check next message.
- rcvgo xgdy We have a message. Put its address in ACCD.
- puly Then set up IY to point to the stack context
- std IX,y Store the message address in the IX register
- * of the receiving task's stack context.
- xgdy Then put the pointer back into IY.
- ldd 0,y Unlink the message from the thread.
- std 0,x
- jmp endtsk Go wrap it up.
-
-
- *******************************************************************************
- * *
- * Dequeue an element from a FIFO queue. This function is used to remove an *
- * element from a FIFO (First in- First out) queue. The queue is divided into *
- * two parts, the queue header and the queue body. The queue header contains *
- * all of the information about the state of the queue body. An attempt to re- *
- * move an entry from an empty queue will place the caller into a wait state. *
- * When the queue has something put into it, the waiting task will be resumed. *
- * Queue entries can be of any size but each queue handles only one size of *
- * entry. *
- * *
- * calling sequence: *
- * *
- * ACCA = Queue number *
- * IX = Destination address of dequeued entry of *
- * size > 2 bytes. Otherwise ignored. *
- * swi *
- * FCB .deque. *
- * return: For an entry size of 1 byte: *
- * ACCA = dequeued entry *
- * For an entry size of 2 bytes: *
- * ACCA = byte 1 of entry *
- * ACCB = byte 2 of entry *
- * Other registers unchanged *
- * *
- *******************************************************************************
-
- .deque bsr qsetup Set up the queue header information
- * The queue depth and width are moved to
- * RAM work area as is the Not Empty sema-
- * phore. The address of the queue body is
- * in RAM at location "temp".
- ldab CURSIZ,x Check current size of queue to see if empty.
- beq qempty Branch if queue is empty.
- pshx Save the queue header address.
- ldaa PIX,x Not empty. Get the Put Index and subtract
- sba the Current Size from it.
- bhs nowrap Check for wrap around.
- adda depth If so, add Depth of queue.
- nowrap ldab width ACCA = Get Index. Load ACCB with Width of
- pshb an entry. Also save the width for later.
- dec CURSIZ,x Decrement the Current Size of the queue by 1
- mul Now multiply the Width times the Get Index
- addd temp and add the base address of the queue body
- xgdx to get address of the entry to dequeue.
- pula Get the Width again
- cmpa #2 Is it > 2 bytes?
- bgt dqgt2 Branch if width > 2 bytes.
- bge dq2 See if Width = 2 bytes and branch if so.
- ldab 0,x Width = 1 byte. Get it.
- **(V1.1 #1)**
- stab ACCA,y Store it in ACCA of the stack context. |
- bra dqend That's it.
- dq2 ldab 0,x For Width = 2 bytes, get the 1st byte
- stab ACCA,y and store it in ACCA of stack context.
- ldab 1,x Then get the 2nd byte and store in in ACCB
- stab ACCB,y of the stack context.
- bra dqend And that's that for size = 2.
- dqgt2 ldy IX,y For Width > 2 bytes, get the destination
- * address from IX of the stack context.
- dqloop ldab 0,x Get next byte from queue body.
- stab 0,y Move it to next byte of the destination.
- inx
- iny
- deca Decrement the Width count.
- bne dqloop Loop until count is = 0.
- dqend pulx When done, get the queue header address.
- ldab QSEMA,x Get the Queue NOT FULL semaphore #
- beq nopost See if anyone is waiting for NOT FULL.
- postem clra There is. Go signal the semaphore.
- staa QSEMA,x But first, set the active semaphore # = 0
- jmp signal2
- qempty addb notmt Queue is empty. Set up a wait state for this
- stab QSEMA,x task on the Queue NOT EMPTY semaphore
- ** (V1.2 #2) **
- backup pshb Save the semaphore number |
- ldd PC,y Here we have to back up the PC by 2 bytes |
- subd #2 so that when the task restarts, it will |
- * be at the SWI used to call MCX11. |
- std PC,y Save the decremented PC |
- pulb Get the semaphore number. |
- ldx #FLGTBL-1 |
- abx Calculate address of semaphore. |
- *** (V1.3 #6) ***
- sei Prepare to enter critical code
- jmp force Go set the wait state and clean up. |
-
- qsetup clra
- aslb Multiply the queue number by the length
- pshb Save queue number * 2.
- addb ACCA,y of the queue initialization data block.
- aslb
- addd #QUEDATA-QUEDATL Then add the address of the queue init data
- xgdx
- pulb Get queue number * 2 and add the number of
- addb #NNAMSEM+NTASKS-1 named semaphores + the number of tasks
- stab notmt to get the NOT EMPTY semaphore #.
- ldd WIDTH,x Get the width and depth of the queue
- std width Store them in the work area of RAM.
- ldd QADDR,x Get the queue body address
- std temp Store it in temp word in RAM
- ldx QHADR,x Get the Queue Header address
- rts Return
-
- *******************************************************************************
- * *
- * Enqueue an element into a FIFO queue. This function is used to insert an *
- * element into a FIFO (First in- First out) queue. The queue is divided into *
- * two parts, the queue header and the queue body. The queue header contains *
- * all of the information about the state of the queue body. An attempt to in- *
- * sert an entry into a full queue will place the caller into a wait state. *
- * When the queue has something taken out of it, the waiting task will be *
- * resumed. Queue entries can be of any size but each queue handles only one *
- * size of entry. *
- * *
- * calling sequence: *
- * *
- * ACCA = Queue number *
- * IX = Source address of entry to be enqueued if *
- * size > 2 bytes. *
- * Or, IX8-15 = byte to be enqueued if size = 1 *
- * Or, IX8-15 = byte 1 and IX0-7 = byte 2 if size = 2 *
- * swi *
- * FCB .enque. *
- * return: Registers are unchanged *
- * *
- *******************************************************************************
-
- .enque bsr qsetup Set up the queue header information
- ldaa depth Compare DEPTH of queue to its Current Size
- cmpa CURSIZ,x to see if it is full.
- beq full Branch if queue is full.
- pshx Save address of the queue header.
- cmpa PIX,x Compare the depth to the Put Index to see
- bgt wrapnot if there is an address wrap around.
- clra
- staa PIX,x If a wrap around, reset Put Index to 0.
- wrapnot ldaa width Then use the width and the Put index to
- psha compute the destination address in the
- ldab PIX,x queue body.
- mul
- addd temp
- inc PIX,x Bump the Put Index
- inc CURSIZ,x Bump the Current Size
- *** (V1.3 #7) ***
- ldy IX,y Get data to be enqueued or pointer to it.
- xgdx Move destination address into IX.
- pula Get the Width again.
- cmpa #2 See if Width > 2 bytes.
- bgt nqloop Branch if Width > 2 bytes
- xgdy If not, the data is in IY. Put it in ACCD.
- beq nq2 Branch if Width = 2 bytes.
- ** (V1.1 #2) **
- staa 0,x Width is 1 byte. Store it from IX8-15.
- bra nqend Then go to wrap up routine.
- nq2 std 0,x For Width of 2 bytes, store from IX0-15.
- bra nqend
- nqloop ldab 0,y For Width > 2 bytes, move a byte from the
- stab 0,x source address to the destination address.
- inx
- iny
- deca Decrement the counter in ACCA.
- bne nqloop Loop until done.
- nqend pulx When all done, get the address of the queue
- ldab QSEMA,x header, and test for a waiter on queue
- * Not Empty.
- bne postem If there is a waiting task, go resume it.
- nopost jmp endtsk If not, just go back to caller.
- full ldab #1 If queue is full, set up a wait on it.
- * Queue Not Full semaphore = Not Empty + 1.
- bra qempty Go set up the Wait condition.
-
- *******************************************************************************
- * *
- * Resume a task. This is the opposite function of .suspnd.. The SUSPEND state *
- * is removed from the specified task. If the removal of the suspension makes *
- * the task runnable, it is scheduled. If it is of higher priority than the *
- * resuming task, a context switch will occur. Note that the task to be *
- * resumed must be explicitly specified by the content of ACCA. A task may not *
- * resume itself, obviously. *
- * *
- * calling sequence: *
- * *
- * ACCA = task number *
- * swi *
- * FCB .resume. *
- * return: All registers unchanged *
- * *
- *******************************************************************************
-
- .resume ldaa #-_SUSPND-1
- jmp clrstat Go clear the task's suspend status.
-
- *******************************************************************************
- * *
- * Suspend a task. This function is used to set a task into the SUSPEND state. *
- * Once in this state, it cannot be made runnable again except by a .resume. *
- * ESR or an .execute. ESR. The task to be suspended is specified by putting *
- * its task number in ACCA. If the current task is suspendeing itself, ACCA *
- * is set to either the task number, if known, or more simply, a 0. *
- * *
- * calling sequence: *
- * *
- * ACCA = task number (Otherwise this must be 0) *
- * swi *
- * FCB .suspend. *
- * return: All registers unchanged *
- * *
- *******************************************************************************
- .suspnd tstb
- bne notcur See if the task to suspend is SELF.
- ldx curtcb It is. Load TCB address.
- bra setspnd
- notcur ldaa #TCBLEN If not current task, compute TCB address
- mul of the specified task.
- addd #STATLS-TCBLEN
- xgdx
- setspnd bset STATE,x _SUSPND Set the SUSPEND state in task's status byte.
- jmp endtsk
-
- *******************************************************************************
- * *
- * Terminate a task. This is a seldom used task but is included for complete- *
- * ness. This function is called when it is desireable to terminate a task. It *
- * may be any task in the system including the caller. If the caller is going *
- * to commit suicide, the given task number is set to 0. As a result of this *
- * ESR, all timers which are in process for the terminated task are purged *
- * the active timer list. The terminated task is set to an IDLE state. Once *
- * terminated, a task may be restarted only by the .xeqt. command. *
- * *
- * calling sequence: *
- * *
- * ACCA = Task number (0 if self) *
- * swi *
- * FCB .terminate. *
- * return only if not terminating self. Registers unchanged *
- * *
- *******************************************************************************
-
- .termn8 tstb Test for self.
- bne selfnot Branch if task to terminate is not self.
- ldab curtsk If self, get current task number.
- ldx curtcb Then set up current task's TCB address.
- stab temp+1 Store in lower half of temp
- bra setidle Then go set up the IDLE state.
- selfnot stab temp+1 Store task # in lower half of temp.
- ldaa #TCBLEN
- mul Compute TCB address of task to be terminated
- addd #STATLS-TCBLEN
- xgdx
- setidle bset STATE,x _IDLE Set task's state to IDLE
- clra
- jmp beginp Then go purge all the task's timers.
-
-
- *******************************************************************************
- * *
- * Execute a task. This function is used to put a task into the state of being *
- * ready to run. Its TCB is initialized so that its stack pointer is set to *
- * the base address assigned to it. A stack frame is allocated on the task's *
- * stack for an initial context. That context is set to contain the task's *
- * starting address as the PC, while CCR, ACCB, and ACCA are cleared. *
- * *
- * calling sequence: *
- * *
- * ACCA = task number (cannot be SELF) *
- * swi *
- * FCB .execute. *
- * return: All registers unchanged *
- * *
- *******************************************************************************
-
- .xeqt pshb Save the task number
- ldaa #TCBDATL Load length of TCB init data block
- mul Multiply it by task number.
- addd #TCBDATA-TCBDATL
- xgdx
- ldd RSTSP,x Get the base address of task's stack space.
- subd #9
- xgdy
- ldd STRTADR,x Set up PC to be the starting address
- ldx TCBADDR,x
- std PC+1,y
- clra
- clrb
- staa CCR+1,y Clear CCR
- std ACCB+1,y Clear ACCB and ACCA
- xgdy Put address of stack context back into D
- std ACTSP,x Store address of the context in the TCB.
- clra
- jmp clrbits Go make the task runnable.
-
- *******************************************************************************
- * *
- * Delay a task for a period of time. This function uses the .timer. ESR *
- * to set up a timer and a wait state on the specified task. When the timer *
- * elapses, the specified task is resumed. *
- * *
- * calling sequence: *
- * *
- * ACCA = Task number (0 if self) *
- * ACCB = Semaphore number (0 if task's semaphore) *
- * IX = Number of clock tocks to delay *
- * swi *
- * FCB .delay. *
- * return. Registers unchanged *
- * *
- *******************************************************************************
-
- .delay ldaa #$FF Set up switch.
-
- *******************************************************************************
- * *
- * Set up a timer for a task. This function is used to set a timer active for *
- * the specified task. The timer is in clock tock units. A clock tock is a *
- * system defined parameter. The timers are 16-bit timers; therefore, the *
- * maximum duration of a timer is determined by the clock tock frequency. *
- * *
- * calling sequence: *
- * *
- * ACCA = Task number (0 if self) *
- * ACCB = Semaphore number (0 if task's semaphore) *
- * IX = Number of clock tocks in timer *
- * swi *
- * FCB .timer. *
- * return. Registers unchanged *
- * ACCA will be current task number if ACCA was = 0 *
- * at the time of the ESR. *
- * *
- *******************************************************************************
-
- .timer inca Set up switch: 0 = .delay., >0 = .timer.
- psha Save the switch.
- tstb Test for task being self.
- bne notself branch if task # is not 0.
- ldx curtcb Get address of current task's TCB
- ldaa curtsk get the current task number.
- staa ACCA,y Save it in the stack context.
- bra gotask
- notself ldaa #TCBLEN
- mul Compute TCB address of specified task.
- addd #STATLS-TCBLEN
- xgdx
- gotask ldab ACCA,y Get the actual task number
- pshb Save the task number.
- pshx Save the TCB address of the specified task.
- ldaa ACCB,y Get the semaphore number to use with timer.
- bne gotsema Branch if semaphore number is defined.
- ldaa ACCA,y If semaphore # = 0, Use task semaphore of
- adda #NNAMSEM
- gotsema psha specified task. Save semaphore number.
- ldd IX,y Get the timer value.
- bmi savrset If timer < 0, go save the timer reset value.
- clra
- clrb Timer is a one-shot. Reset time = 0.
- savrset pshb Save the reset time.
- psha
- ldd IX,y Get the timer (as specified) again.
- bpl savtime See if it is cyclic or one-shot.
- coma If cyclic, be sure it is positive.
- negb
- bcs savtime
- inca
- savtime ldx #ACTIVE Let IX contain the address of the active
- sei timers.
- tloop ldy CLINK,x Get address of next timer.
- beq append If end-of-list, go append new timer.
- cpd CTOCKS,y There is a timer that is active. Compare its
- * timer counts with those in the new timer.
- blt nsrtm If there are fewer tocks in the new timer,
- * go insert the new timer in front of the
- * active one.
- subd CTOCKS,y If not, subtract the tocks of the active
- * timer from the new timer.
- ldx CLINK,x Then move down the thread.
- bra tloop
- nsrtm pshb Save the remaining time in the timer.
- psha
- subd CTOCKS,y Update the active timer in the list
- coma subtracting the new timer's residual
- negb from it.
- bcs updt
- inca
- updt std CTOCKS,y Store the updated timer.
- bra linkit
- append pshb Save the timer.
- psha
- linkit ldd CLINK,x Get the address of the current active timer.
- pshb
- psha Save the address
- ldd FREE Get the address of the next free timer block
- std CLINK,x Store it in link of the active timer block.
- xgdy IY now contains address of the new timer blk
- ldd CLINK,y Get address of next free timer block.
- std FREE Save it in pointer.
- pula
- pulb Get address of the current active timer.
- std CLINK,y Store it in link word of the new timer block
- pula
- pulb
- std CTOCKS,y Store timer tocks in new timer block.
- pula
- pulb
- std CRESET,y Store timer reset value.
- pulb
- stab CSEMA,y Store semaphore #
- pulx Get TCB address
- pula
- staa CTASK,y Store # of the task using the timer.
- pula Get the switch
- tsta
- beq dlay Branch if switch set for .delay.
- jmp dopend ** (V1.1 #3) **
- dlay bset STATE,x _WAIT If .delay., set wait state in task.
- ldx #FLGTBL-1
- abx Compute address of semaphore.
- ldaa CTASK,y Get the # of the task being delayed.
- nega
- staa 0,x Set the semaphore to WAIT state.
- jmp endtsk
-
- *******************************************************************************
- * *
- * Purge a timer or timers. This function is used to purge one timer for a *
- * given task and semaphore combination or all of the timers for a given task. *
- * Each timer so purged has its associated semaphore reset to the PENDing *
- * state. *
- * *
- * calling sequence: *
- * *
- * ACCA = Task number (0 if self) *
- * ACCB = Semaphore number (0 if task's semaphore) *
- * IX0-7= Switch: 0 = purge all timers for given task *
- * >0 = purge timer matching task and sema *
- * swi *
- * FCB .purge. *
- * return. Registers unchanged *
- * *
- *******************************************************************************
-
- .purge tstb
- bne gotsk If ACCB > 0, task is specific.
- ldab curtsk Otherwise, use current task.
- gotsk std temp Save task and semaphore numbers.
- chksema clra
- tst IXL,y Test the switch for type of purge.
- beq beginp Branch if switch = 0.
- ldaa temp Otherwise get the semaphore number to match.
- bne beginp Branch if it is defined.
- ldaa temp+1 Otherwise use the task semaphore.
- adda #NNAMSEM
- beginp ldab temp+1 Load the task number to be matched.
- ldx #ACTIVE Set up pointer to 1st active timer.
- sei Interrupts off during this next phase.
- ploop ldy CLINK,x Set IY = address of next active timer.
- beq endpurg If end-of-list, purge is complete.
- cmpb CTASK,y Compare task # in active timer to object #.
- beq sametsk Branch if both task numbers are equal.
- chknxt ldx CLINK,x If not equal, walk list to next active timer
- bra ploop Keep looking.
- sametsk tsta Tasks are equal. Test if semaphore # = 0.
- beq remove Branch if semaphore # = 0.
- cmpa CSEMA,y If semaphore > 0, Check it against semaphore
- bne chknxt in active timer. Branch if not the same.
- remove pshb
- psha Save semaphore and task number.
- pshx Save IX for a moment.
- ldx #FLGTBL-1
- ldab CSEMA,y
- abx Compute address of timer's semaphore.
- ldaa #_PEND
- staa 0,x Set timer's semaphore back to PENDing.
- pulx Restore IX
- ldd CLINK,y Get the pointer to next active timer block.
- std CLINK,x Store it in link word of pointer to current
- * active timer.
- *** (V1.4 #3) *** |
- beq prgexit |
- ldd CTOCKS,y Update the next timer in link |
- *** (V1.5 #1) *** +
- pshy Save y +
- ldy CLINK,y |
- addd CTOCKS,y |
- std CTOCKS,y |
- puly Restore Y +
- prgexit ldd FREE |
- std CLINK,y Make current timer block the first free one.
- xgdy
- std FREE
- pula
- pulb Restore task and semaphore numbers.
- tsta Test the semaphore number.
- beq ploop Branch if semaphore = 0. (purge all timers)
- endpurg jmp endtsk Otherwise that's all.
-
-