Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

73KB

Developing Your Own 32-Bit Operating System — Chapter-18: Kernel Code

  • Source: /library/Developing Your Own 32-Bit Operating System/source-file.pdf
  • PDF pages: 357–398
  • Pages without text: 357, 398

18 Kernel Code

[336 Part PV ■ The Operating System Source Code The kernel code is very small. The actual message handling functions, the scheduler, and the associated kernel helper procedures turn out to be less than 3K of executable code. The code in the kernel is divided into four main sections: data, local helper functions, internal public functions, and kernel publics functions. Being an old “structured” programmer, I have made most of the “called” functions reside above those that call them. If you read through the descriptions of these little “helper” routines before you look at the code for the kernel primi- tives, I think the code will make more sense to you. Naming Conventions For helper routines that are called only from other assembly language routines, the name of the routine is unmodified (no underscores used). For functions that must be reached by high-level languages (such as C), a single underscore is prepended to the name. For public functions that are reached through call gates from applica- tions, two underscores are prepended to the name. Kernel Data The source file for the kernel doesn't really contain any data variable declarations. The bulk of the data variable declarations are in the file Main.ASM. The data that the kernel primitives deal with are usually used in context to the task that is running. These data are things such as the task state segments, job control blocks, and memory management tables. These all change with respect to the task that is running. The bulk of the other data items are things for statistics: the number of task switches (_nSwitches); the number of times the CPU found itself with nothing to do (_nHalts); and the number of tasks ready to run (_nReady). Variables named with a leading underscore are accessible from code generated with the C com- piler. For the most pan, these are statistic-gathering variables, so I could gain access to them with the monitor program. The INCLUDE files define things such as error codes and also offsets into structures and tables. See Listing 18.1. Listing 18.1. Kernel data segment source. .DATA .INCLUDE MOSEDF.INC .INCLUDE TSS.INC .INCLUDE ROB. INC .INCLUDE JOB. INC dJunk DD 0 ;Used as temp in Service Abort function

Chapter 18 ■ Kernel Code EXTRN TimerTick DD EXTRN SwitchTick DD; EXTRN dfHalted DD EXTRN _nSwitches DD EXTRN _nHaltS DD EXTRN _nReady DD Local Kernel Helper Functions This section has all of the little functions that help the kernel primitives and scheduler code. Most of these functions manipulate all the linked lists that maintain things such as the ready queue, link blocks, and exchanges. The interfaces to these calls are exclusively through registers. The issue here is strictly speed. It's not that the routines are called so often; it's that they manipulate common data and there- fore are not re-entrant, which means interrupts must be cleared. I also had to keep a very good track of which registers I used. Because certain kernel functions may be called from ISRs, and because portions of other ker- nel functions may be interrupted by a task change that happens as a result of an action that an ISR takes, you must ensure that interrupts are disab led ‘prior to the allocation or deallocation of ^//kernel data segment resources. This especially applies when a message is “in transit” (taken, for example, from an exchange but not yet linked to a TSS and placed on the ready queue). This is important. The concept itself should be very important to you if you intend to write your own multitasking operating system. The functions described have a certain symmetry that you'll notice, such as enQueueMsg and deQueueMsg along with enQueueRdy and deQueueRdy. You may notice that there is a deQueueTSS but no enQueueTSS. This is because the enQueueTSS functionality is needed only in two places and, therefore, is coded in-line. The following lines begin the code segment for the kernel. .CODE EXTRN LinToPhy NEAR enQueueMsg The enQueueMsg function places a link block containing a message on an exchange. The mes- sage can be a pointer to a Request or an 8-byte generic message. Interrupts will already be dis- abled when this routine is called. In MMURTL, an exchange is a place where either messages or tasks wait (link blocks that contain the message actually wait there). There can never be tasks and messages at an exchange at the same time (unless the kernel is broken!). When a message is sent to an exchange, and if a task

Part PV ■ The Operating System Source Code is waiting there, the task is immediately associated with the message and placed on the ready queue in priority order. For this reason, you share the HEAD and TAIL link pointers of an exchange for tasks and messages. A flag tells you whether it's a task on a message. See Listing 18.2. Listing 18.2. Queueing for messages at an exchange. enQueueMsg: INPUT : ESI, EAX OUTPUT : NONE REGISTERS : EAX, EDX, ESI , FLAGS MODIFIES : EDX This routine will place the link block pointed to by EAX onto the exchange pointed to by the ESI register. If EAX is NIL then the routine returns. OR EAX, EAX ; if pLBin = NIL THEN Return; JZ eqMsgDone ; MOV DWORD PTR [EAX+NextLB] , 0 ; pLBin’.Next <= NIL; XCHG ESI, EAX ; pExch => EAX, pLBin => ESI CMP DWORD PTR [EAX+EHead], 0 ; if . .MsgHead = NIL JNE eqMNotNIL ; then MOV [EAX+EHead], ESI ; ..MsgHead <= pLBin; MOV [EAX+ETail],ESI ; . .MsgTail <= pLBin; MOV DWORD PTR [EAX+fEMsg], 1 ; Flag it as a Msg (vice a task) XCHG EAX, ESI ; Put pExch Back in ESI RETN ; else eqMNotNIL: MOV EDX,[EAX+ETail] ; . .MsgTail’ .NextLB <= pLBin ; MOV [EDX+NextLB],ESI ; MOV [EAX+ETail],ESI ; ..MsgTail <= pLBin; MOV DWORD PTR [EAX+fEMsg], 1 ; Flag it as a Msg (vice a task) XCHG EAX, ESI ; Put pExch Back in ESI eqMsgDone: RETN ; deQueueMsg The deQueueMsg function removes a link block from an exchange if one exists there. If not, deQueueMsg returns NIL (0) in the EAX register. As stated before, exchanges can hold messages or tasks, but not both. You check the flag in the exchange structure to see which is there. If what's there is a message, you remove it from the linked list and return it. If not, you return NIL (0). See Listing 18.3. Listing 18.3. De-queueing a message from an exchange. deQueueMsg:

Chapter 18 ■ Kernel Code H INPUT : ESI OUTPUT : EAX REGISTERS : EAX, EBX, ESI , FLAGS MODIFIES : prgExch[ESI ] .msg. head and EBX This routine will dequeue a link block on the exchange pointed to by the ESI register and place the pointer to the link block dequeued into EAX. MOV EAX,[ESI+fEMsg] OR EAX, EAX JZ deMsgDone MOV EAX,[ESI+EHead] OR EAX, EAX JZ deMsgDone MOV EBX,[EAX+NextLB] MOV [ESI+EHead],EBX Get Msg Flag Is it a Msg? Nol (return 0) pLBout <= . .MsgHead; if pLBout = NIL then Return; MsgHead MsgHead .Next: deMsgDone: RETN deQueueTSS The deQueueTSS function removes a pointer to a TSS from an exchange if one exists there. If not, it returns NIL (0) in the EAX register. See Listing 18.4. Listing 18.4. De-queueing a task from an exchange. deQueueTSS: INPUT : ESI OUTPUT : EAX REGISTERS : EAX, EBX, ESI , FLAGS MODIFIES : EAX, EBX This routine will dequeue a TSS on the exchange pointed to by the ESI register and place the pointer to the TSS dequeued into EAX. EAX return NIL if no TSS is waiting at Exch ESI XOR EAX, EAX MOV EBX,[ESI+fEMsg] OR EBX, EBX JNZ deTSSDone MOV EAX,[ESI+EHead] OR EAX, EAX JZ deTSSDone MOV EBX,[EAX+NextTSS] MOV [ESI+EHead],EBX Set up to return nothing Msg flag (is it a Msg) It's a Msg (return leaving EAX 0) pTSSout <= ..TSSHead; if pTSSout = NIL then Return; .TSSHead .TSSHead^.Next; deTSSDone: RETN

H Part TV ■ 77^ Operating System Source Code enQueueRdy The enQueueRdy function places a task (actually a TSS) on the ready queue. The ready queue is a structure of 32 linked lists, one for each of the possible task priorities in MMURTL. You dereference the TSS priority and place the TSS on the proper linked list. See Listing 18.5. Listing 18.5. Adding a task to the ready queue. PUBLIC enQueueRdy: INPUT : EAX OUTPUT : NONE REGISTERS : EAX , EBX , EDX , FLAGS MODIFIES : EAX, EBX, EDX This routine will place a TSS pointed to by EAX onto the ReadyQueue. This algorithm chooses the proper priority queue based on the TSS priority. The Rdy Queue is an array of QUEUES (2 pointers, head & tail per QUEUE). This links the TSS to rgQueue[nPRI] . OR EAX , EAX JZ eqRdyDone INC _nReady MOV DWORD PTR XOR EBX, EBX MOV BL,[EAX+Priority] XCHG EAX, EBX SHL EAX, 3 LEA EDX.RdyQ ADD EAX, EDX CMP DWORD PTR [EAX+Head] JNE eqRNotNIL MOV [EAX+Head], EBX MOV [EAX+Tail],EBX RETN if pTSS = NIL then return; [EAX+NextTSS], 0 ; pTSSin*. Next <= NIL; get the priority in EBX Priority => EAX, pTSSin => EBX Times 8 (size of QUEUE) Add offset of RdyQ => EAX EAX pts to proper Rdy Queue 0 ; if Head = NIL then . .Head <= pTSSin; . .Tail <= pTSSin; else eqRNotNIL: MOV EDX,[EAX+Tail] MOV [EDX+NextTSS],EBX MOV [EAX+Tail],EBX eqRdyDone: RETN .Tail’.NextTSS <= pTSSin; .Tail <= pTSSin; deQueueRdy The deQueueRdy function finds the highest-priority ready queue (out of 32) that has a task waiting there and returns a pointer to the first TSS in that list. The pointer to this TSS is also removed from the list. If none is found, the function returns NIL (0). Keep in mind that 0 is the highest priority and 31 is the lowest. See Listing 18.6.

Chapter 18 Kernel Code H Listing 18.6. De-queueing the highest priority task. PUBLIC deQueueRdy: INPUT : NONE OUTPUT : EAX REGISTERS : EAX, EBX, ECX, FLAGS MODIFIES : RdyQ This routine will return a pointer in EAX to the highest priority task queued on the RdyQ. Then the routine will “pop” the TSS from the RdyQ. If there was no task queued, EAX is returned as NIL. MOV ECX nPRI LEA EBX RdyQ deRdyLoop: MOV EAX [EBX] OR EAX EAX JNZ deRdyFound ADD EBX SQUEUE LOOP deRdyLoop deRdyFound: OR EAX EAX JZ deRdyDone DEC nReady MOV ECX [EAX+NextTSS] MOV [EBX], ECX deRdyDone: RETN Set up the number of times to loop Get base address of RdyQ in EBX Get pTSSout in EAX IF pTSSout is NIL Then go and check the next priority. Point to the next Priority Queue DEC ECX and LOOP IF NOT ZERO IF pTSSout is NIL Then there are No TSSs on the RdyQ; RETURN Otherwise, deQueue the process And return with the pointer in EAX ChkRdyQ MMURTL's preemptive nature requires that you have the ability to check the ready queues to see what the highest priority task is that could be executed, without actually removing the task from the queue. This routine provides that functionality for the piece of code in the timer in- terrupt routine (in TimerCode.ASM) that provides preemptive task switching. This is why it's specified as PUBLIC. See Listing 18.7. Listing 18.7. Finding the highest priority task. PUBLIC ChkRdyQ: INPUT : NONE OUTPUT : EAX REGISTERS : EAX, EBX, ECX, FLAGS MODIFIES : RdyQ This routine will return a pointer to the highest priority TSS that

[342 Part IV The Operating System Source Code Listing 18.7. continued is queued to run. It WILL NOT remove it from the Queue, If there was no task queued, EAX is returned as NIL. MOV ECX.nPRI LEA EBX.RdyQ ChkRdyLoop: MOV EAX,[EBX] OR EAX, EAX JNZ ChkRdyDone ADD EBX.sQUEUE LOOP ChkRdyLoop ChkRdyDone: RETN ; Set up the number of times to loop ; Get base address of RdyQ in EBX ; Get pTSSout in EAX ; IF pTSSout is NIL Then go and ; check the next priority. ; Point to the next Priority Queue DEC ECX and LOOP IF NOT ZERO Internal Public Helper Functions The routines described in the following sections are “public” to the rest of the operating sys- tem but not accessible to outside callers. They are located in Kernel.ASM because they work closely with kernel structures. They provide things such as resource garbage collection and exchange owner manipulation. RemoveRdyjob When a job terminates, either by its own choosing or by being killed off due to some unforgiv- able protection violation, the RemoveRdyjob function recovers the task state segments (TSSs) that belonged to that job. “It's a nasty job, but someone's got to it.” See Listing 18.8. Listing 18.8. Removing a terminated task from the ready queue. RemoveRdyjob (NEAR) This routine searches all ready queue priorities for tasks belonging to pJCB. When one is found it is removed from the queue and the TSS is freed up. This is called when we are killing a job. Procedural Interface : RemoveRdyJob(char *pJCB) :ercType pJCB is a pointer to the JCB that the tasks to kill belong to. pJCB EQU DWORD PTR [EBP+8] INPUT : OUTPUT (pJCB on stack) NONE

Chapter 18 ■ Kernel Code E REGISTERS : All general registers are trashed MODIFIES : RdyQ PUBLIC _RemoveRdyJob: PUSH EBP MOV EBP, ESP MOV ECX.nPRI LEA EBX, RdyQ MOV EDX, [EBP+8] Set up the number of times to loop Get base address of RdyQ in EBX EDX holds pJCB for comparison ;EBX points to beginning of next Priority Queue RemRdyLoop: MOV EAX,[EBX+Head] MOV EDI, EAX OR EAX, EAX JNZ RemRdyO RemRdyLoopI : MOV [EBX+Tail], EDI ADD EBX.sQUEUE LOOP RemRdyLoop Get pTSS in EAX EDI points to last TSS by default (or NIL) Is pTSS 0 (none left queued here) Valid pTSS! ; EDI always points to last TSS or NIL ; Point to the next Priority Queue ; DEC ECX and LOOP IF NOT ZERO XOR EAX, EAX POP EBP RETN 4 ; No error ; All done (clean stack) ;Go here to dequeue a TSS at head of list RemRdyO: CMP EDX, [EAX+TSS_pJCB] JNE RemRdy2 MOV EDI, [EAX+NextTSS] MOV [EBX+Head], EDI PUSH EBX Is this from the JCB we want? No Yes, deQueue the TSS Fix link in Queue list Save ptr to RdyQue (crnt priority) ;Free up the TSS (add it to the free list) MOV EBX.pFreeTSS ; pTSSin’.Next <= pFreeTSS; MOV [EAX+NextTSS], EBX ; MOV DWORD PTR [EAX+TSS_pJCB] , 0 ; Make TSS invalid MOV pFreeTSS.EAX INC nTSSLeft pFreeTSS <= pTSSin; POP EBX MOV EAX, EDI OR EAX, EAX JZ RemRdyLoopI JMP RemRdyO Make EAX point to new head TSS Is it Zero? Next Queue please back to check next at head of list ;Go here to dequeue a TSS in middle or end of list RemRdy2: MOV EAX, [EDI+NextTSS] OR EAX, EAX JZ RemRdyLoopI Get next link in list Valid pTSS? No. Next Queue please

H o / / Prfrf TV ■ 77?? Operating System Source Code Listing 18.8. continued CMP EDX, [EAX+TSS_pJCB] ; Is this from JCB we want? JE RemRdy3 ; Yes. Trash it. MOV EDI, EAX ; No. Next TSS JMP RemRdy2 RemRdy3: ;EDI points to prev TSS ;EAX points to crnt TSS ;Make ESI point to NextTSS MOV ESI, [EAX+NextTSS] ; Yes, deQueue the TSS ;Now we fix the list (Make Prev point to Next) ;This extracts EAX from the list MOV [EDI+NextTSS], ESI ;Jump the removed link PUSH EBX ;Save ptr to RdyQue (crnt priority) ;Free up the TSS (add it to the free list) MOV EBX, pFreeTSS ; pTSSin’.Next <= pFreeTSS; MOV [EAX+NextTSS], EBX MOV DWORD PTR [EAX+TSS_pJCB] , 0 MOV pFreeTSS, EAX Make TSS invalid pFreeTSS <= pTSSin; INC _nTSSLeft POP EBX OR ESI, ESI ;Is EDI the new Tail? (ESI JZ RemRdyLoopI ;Yes. Next Queue please JMP RemRdy2 ;back to check next TSS GetExchOwner The code that loads new jobs must have the capability to allocate default exchanges for the new program. The Exchange allocation routines assume that the caller will own this exchange. This is not so if it's for a new job. This call identifies who owns an exchange by returning a pointer to the job control block. This function is used with the next one, SetExchOwner( ). These are NEAR functions and not available to outside callers (via call gates). See Listing 18.9. Listing 18.9. Finding the owner of an exchange. GetExchOwner (NEAR) This routine returns the owner of the exchange specified. A pointer to the JCB of the owner is returned. ErcNotAlloc is returned if the exchange isn't allocated. ErcOutofRange is returned is the exchange number is invalid (too high) Procedural Interface : GetExchOwner(long Exch, char *pJCBRet): dErrror

Chapter 18 ■ Kernel Code H Exch is the exchange number. pJCBRet is a pointer to the JCB that the tasks to kill belong to. Exch pJCBRet EQU DWORD PTR [EBP+12] EQU DWORD PTR [EBP+8] PUBLIC _GetExchOwner: PUSH EBP MOV EBP, ESP MOV EAX, [EBP+12] CMP EAX.nExch JB GEO01 MOV EAX .ErcOutOf Range JMP GEOEnd Get Resp Exchange in EDX Is the exchange out of range? No, continue Yes, Error in EAX register MOV EDX.sEXCH MUL EDX MOV EDX.prgExch ADD EDX, EAX MOV EAX, [EDX+Owner] OR EAX, EAX JNZ GEO02 MOV EAX, ErcNotAlloc JMP SHORT GEOEnd Compute offset of Exch in rgExch sExch * Exch number Add offset of rgExch => EAX EDX -> Exch Valid Exch (Allocated) No, not allocated GEO02: GEOEnd: MOV ESI, [EBP+8] MOV [ESI], EAX XOR EAX, EAX MOV ESP, EBP POP EBP RETN 8 ;Where to return pJCB of Exchange SetExchOwner The SetExchOwner ( ) function is the complimentary call to GetExchOwner( ) , mentioned previ- ously. This call sets the exchange owner to the owner of the job control block pointed to by the second parameter of the call. See Listing 18.10. Listing 1 8.1 0. Changing the owner of an exchange. SetExchOwner (NEAR) This routine sets the owner of the exchange specified to the pJCB specified. This is used by the Job code to set the owner of a TSS exchange to a new JCB (even though the exchange was allocated by the OS). No error checking is done because the job code does it upfront!

346| Part PV The Operating System Source Code Listing 18.10. continued Procedural Interface : SetExchOwner(long Exch, char *pNewJCB) : dErrror Exch is the exchange number. pNewJCB is a pointer to the JCB of the new owner. Exch EQU DWORD PTR [EBP+12] pNewJCB EQU DWORD PTR [EBP+8 BLIC SetExchOwner: PUSH EBP MOV EBP, ESP MOV EAX, [EBP+12] Exchange Number MOV EDX.sEXCH Compute offset of Exch in rgExch MUL EDX sExch * Exch number MOV EDX.prgExch Add offset of rgExch => EAX ADD EAX, EDX EAX -> oExch + prgExch MOV EBX, [EBP+8] MOV [EAX+Owner], EBX XOR EAX, EAX POP EBP RETN 8 SendAbort System services are programs that respond to requests from applications, other services, or the operating system itself. System services may hold requests from many jobs at a time until they can service the requests (known as asynchronous servicing). If a program sent a request, then exited or was killed offbecause of nasty behavior, the SendAbort function is called to send a message to all active services, telling them that a particular job has died. If a service is holding a request from the job that died, the service should respond to it as soon as it gets the abort message. It should not process any data for the program (because the program is dead). The error it returns is of no consequence. The kernel knows the program is already dead and will reclaim the request block and exchanges that were used. See Listing 18.11. Listing 1 8.1 1 . Notifying services of a job's demise. SendAbort (NEAR) This routine sends one abort message to each valid service with the jobnum of the aborting job. If we receive a kernel error on Request it may be because it is a service that is aborting itself. We ignore the kernel errors. Procedural Interface :

Chapter 18 Kernel Code E SendAbort(long JobNum, ValidExch): dErrror JobNum is the job that is aborting ValidExch is any valid exchange so the request will go through JobNum EQU DWORD PTR [EBP+12] ValidExch EQU DWORD PTR [EBP+8] PUBLIC _SendAbort: PUSH EBP MOV EBP, ESP MOV ESI, OFFSET rgSVC MOV ECX.nSVC Get the address of rgSVC Get the number of Service Descriptors SAB01 CMP DWORD PTR JE SAB05 [ESI], 0 Valid name? NO, next service SAB02 : PUSH ESI PUSH ECX ;Push all the params to make the request Save count and pointer to SVC name PUSH ESI ;pName PUSH 0 ;Abort Service Code MOV EAX, [EBP+8] ;Exchange PUSH EAX PUSH OFFSET dJunk jpHandleRet PUSH 0 ;npSend PUSH 0 ;pData0 PUSH 0 ;cbData0 PUSH 0 ;pData1 PUSH 0 ;cbData1 MOV EAX, [EBP+12] ;JobNum PUSH EAX ;dData0 PUSH 0 ;dData1 PUSH 0 ;dData2 CALL FWORD PTR Request PUSH DWORD PTR [EBP+8] PUSH OFFSET dmsg CALL FWORD PTR _WaitMsg MOV EAX, dmsg CMP EAX, dJunk ;see if we got response back! JNE SAB02 ;NO - wait again ;Get count and ptr to SVC names back from stack POP ECX POP ESI SAB05 : ADD ESI, SSVC LOOP SAB01 XOR EAX, EAX MOV ESP, EBP POP EBP RETN 8 ;Next Service name

[348 Part PV ■ The Operating System Source Code Public Kernel Functions The remainder of the code defines the kernel public functions that are called through call gates. The public kernel functions are called with 48-bit (FAR) pointers that include the selector of the call gate. Some of the functions are auxiliary functions for outside callers and are not actu- ally part of the kernel code that defines the tasking model; these are functions such as GetPriority ( ). They're in this file because they work closely with the kernel. The offsets to the stack parameters are defined in the comments for each of the calls. RequestQ The kernel Request primitive sends a message like the Send ( ) primitive, except that this func- tion requires several more parameters. A system structure called a request block is allocated and some of the required parameters are placed in it. A request block is the basic structure used for client/server communications. The exchange where a request should be queued is determined by searching the system-service array for a matching request service name specified in the re- quest block. The function that searches the array is GetExchange ( ) , and code for it is in the file SVCCode.asm. See Listing 18.12. Listing 18.12. Request kernel primitive code. ; The procedural interface to Request looks like this: ; Request ( pSvcName [EBP+56] ; wSvcCode [EBP+52] ; dRespExch [EBP+48] ; pRqHndlRet [EBP+44] ; dnpSend [EBP+40] ; pDatal [EBP+36] ; dcbDatal [EBP+32] ; pData2 [EBP+28] ; dcbData2 [EBP+24] ; dDataO [EBP+28] ; dDatal [EBP+16] ; dData2 [EBP+12] ) : dError PUBLIC _Request: PUSH EBP ; Save the Previous FramePtr MOV EBP, ESP ; Set up New FramePtr ;Validate service name from registry and get exchange MOV EAX, [EBP+56] ;pServiceName CALL GetExchange ; Leaves Service Exch in ESI if no errors OR EAX , EAX ;Any errors? JZ SHORT Req02 ;No JMP ReqEnd ;Yes, return error Req02: ;Validate exchange

Chapter 18 ■ Kernel Code S MOV EDX, [EBP+48] CMP EDX.nExch JB Req03 MOV EAX.ercOutOfRange JMP ReqEnd Get Resp Exchange in EDX Is the exchange out of range? No, continue Yes, Error in EAX register Req03: Req04 ;Get them a request block CLI CALL NewRQB ;EAX has ptr to new RqBlk (or 0 if none) ST I OR EAX, EAX ;Did we get one? (NIL (0) means we didn't) JNZ Req04 ;Yes. EAX ptr to new RqBlk MOV EAX, ErcNoMoreRqBlks ;No, Sorry... JMP ReqEnd ; ;ESI still has the exchange for the service ;EDX still has the response exchange ;EAX has pRqBlk (Handle) MOV EBX, EAX MOV [EBX+ServiceExch], ESI MOV EAX, [EBP+52] MOV [EBX+ServiceCode], AX MOV [EBX+RespExch], EDX CALL GetCrntJobNum MOV [EBX+RqOwnerJob], EAX MOV EAX, [EBP+20] MOV [EBX+dData0], EAX MOV EAX, [EBP+16] MOV [EBX+dData1], EAX MOV EAX, [EBP+20] MOV [EBX+dData2], EAX MOV EAX, [EBP+36] MOV [EBX+pData1], EAX MOV EAX, [EBP+32] MOV [EBX+cbData1], EAX MOV EAX, [EBP+28] MOV [EBX+pData2], EAX MOV EAX, [EBP+24] MOV [EBX+cbData2], EAX MOV EAX, [EBP+40] CMP EAX, 3 JB Req06 MOV EAX, 2 EBX now pts to RqBlk Put Svc Exch into RqBlk Get Svc Code Put Svc Code into RqBlk Put Resp Exch into RqBlk Get crnt JCB (Job Num of owner) put in RqBlk Get dData0 put in RqBlk Get dDatal put in RqBlk Get dData2 put in RqBlk Get pDatal put in RqBlk Get cbDatal put in RqBlk Get pData2 put in RqBlk Get cbData2 put in RqBlk Number of Send PbCbs Must be 2 or less MOV [EBX+npSend], AL MOV CL, 2 SUB CL, AL MOV [EBX+npRecv], CL ;Put nSend PbCbs into RqBlk ;Caculate nRecv (2-nSend) ; Leave in CL ;Put npRecv in RqBlk ;At this point the RqBlk is all filled in. ;Now we will return the RqBlkHandle to the user. ;The handle is actually a ptr to the RqBlk but they can't ;it as one anyway (so no problem)

I
350 Part PV ■ The Operating System Source Code Listing 18.12. continued MOV EDI, [EBP+44] MOV [EDI], EBX MOV EDX, EBX ;Ptr to return handle to ;Give it to them ;Save RqBlk in EDX CLI ; No interruptions from here on ;Now we allocate a Link block to use MOV EAX, pFreeLB OR EAX.EAX JNZ Req08 CALL DisposeRQB MOV EAX.ercNoMoreLBs JMP ReqEnd EAX <= pFreeLB; Is pFreeLB NIL? (out of LBs) NO. . . free up RqBlk Move error in the EAX register Go home with bad news MOV EBX,[EAX+NextLB] MOV pFreeLB, EBX DEC nLBLeft pFreeLB <= pFreeLB* .Next MOV DWORD PTR [EAX+LBType] ,REQLB MOV DWORD PTR [EAX+NextLB] , 0 MOV [EAX+DataLo],EDX MOV DWORD PTR [EAX+DataHi] , 0 PUSH EAX : ; This is a Request Link Block ; pLBA.Next <= NIL; ; RqHandle into Lower 1/2 of Msg ; Store zero in upper half of pLB’.Data Save pLB on the stack ;ESI still has the exchange Number for the service. ;The ptr to the exch is required for deQueueTSS so we get it. MOV EAX, ESI MOV EDX, SEXCH MUL EDX MOV EDX.prgExch ADD EAX, EDX MOV ESI, EAX Exch => EAX Compute offset of Exch in rgExch Add offset of rgExch => EAX MAKE ESI <= pExch ;ESI now points to the exchange CALL deQueueTSS OR EAX , EAX JNZ Req10 POP EAX CALL enQueueMsg XOR EAX, EAX JMP SHORT ReqEnd DeQueue a TSS on that Exch Did we get one? Yes, give up the message No, Get the pLB just saved EnQueue the Message on Exch No Error And get out! Req10: POP EBX MOV [EAX+pLBRet] , CALL enQueueRdy MOV EAX.pRunTSS CALL enQueueRdy CALL deQueueRdy CMP EAX.pRunTSS JNE Req12 XOR EAX, EAX JMP SHORT ReqEnd EBX Get the pLB just saved into EBX and put it in the TSS EnQueue the TSS on the RdyQ Get the Ptr To the Running TSS and put him on the RdyQ Get high priority TSS off the RdyQ If the high priority TSS is the same as the Running TSS then return Return to Caller with ere ok.

Chapter 18 M Kernel Code fTcT Req12: ReqEnd: MOV pRunTSS.EAX ; MOV BX,[EAX+Tid] MOV TSS Sel.BX ; INC nSwitches ; MOV EAX, TimerTick ; MOV SwitchTick, EAX ; JMP FWORD PTR [TSS] ; XOR EAX , EAX ; ST I MOV ESP, EBP ; POP EBP ; RETF 48 ; Make the TSS in EAX the Running TSS Get the task Id (TR) Put it in the JumpAddr for Task Switch Keep track of how many switches for stats Save time of this switch for scheduler JMP TSS (This is the task switch) Return to Caller with ere ok. Rtn to Caller & Remove Params from stack RespondQ The Respond primitive is used by system services to respond to a Request ( ) received at their service exchange. The request block handle must be supplied along with the error/status code to be returned to the caller. This call is very similar to Send ( ) except that it de-aliases addresses in the request block and then deallocates it. The exchange to respond to is located inside the request block. See Listing 18.13. Listing 18.13. Respond kernel primitive code. Respond(dRqHndl, dStatRet): dError dRqHndl EQU DWORD PTR [EBP+16] dStatRet EQU DWORD PTR [EBP+12] PUBLIC _Respond: PUSH EBP MOV EBP, ESP MOV EAX, dRqHndl MOV EBX, dStatRet CMP EBX, ErcOwnerAbort JNE Resp01 CLI CALL DisposeRQB XOR EAX, EAX JMP RespEnd Resp01 MOV ESI, [EAX+RespExch] CMP ESI.nExch JNAE Resp02 MOV EAX, ercOutOf Range JMP RespEnd Save Callers Frame Setup Local Frame pRqBlk into EAX No interruptions Return Aborted RQB to pool. No Error Get out Response Exchange into ESI Is the exchange out of range? No, continue Error into the EAX register. Get out

I
352 Part PV ■ The Operating System Source Code Listing 18.13. continued Resp02: Resp04: Resp05: Resp06: MOV EAX, ESI MOV EDX.sEXCH MUL EDX MOV EDX.prgExch ADD EAX, EDX MOV ESI, EAX Exch => EAX Compute offset of Exch in rgExch Add offset of rgExch => EAX MAKE ESI <= pExch CMP DWORD PTR [EAX+Owner], 0 If the exchange is not allocated JNE Resp04 MOV EAX.ercNotAlloc JMP RespEnd return to the caller with error in the EAX register. MOV EAX, dRqHndl ; Get Request handle into EBX (pRqBlk) MOV EBX, [EAX+RqOwnerJob] CALL GetCrntJobNum CMP EAX, EBX JE Resp06 MOV EAX, dRqHndl MOV EBX, [EAX+cbData1] OR EBX, EBX JZ Resp05 MOV EDX, [EAX+pData1] OR EDX, EDX JZ Resp05 ;Same job - no DeAlias needed ; Get Request handle into EBX (pRqBlk) ;No need to dealias (zero bytes) ;Null pointer! PUSH ESI PUSH EDX PUSH EBX ; CALL GetCrntJobNum PUSH EAX CALL FWORD PTR _DeAliasMe POP ESI ; ;Save pExch across call ;pMem ;cbMem m ;DO it and ignore errors get pExch back Get Request handle into EBX (pRqBlk) No need to dealias (zero bytes) MOV EAX, dRqHndl MOV EBX, [EAX+cbData2] OR EBX, EBX JZ Resp06 MOV EDX, [EAX+pData2] OR EDX, EDX JZ Resp06 PUSH ESI PUSH EDX PUSH EBX CALL GetCrntJobNum ; PUSH EAX CALL FWORD PTR _DeAliasMem ;DO it and ignore errors POP ESI ;get pExch back ;Null pointer! ;Save pExch across call ;pMem ;cbMem MOV EAX, dRqHndl CLI CALL DisposeRQB Get Request handle into EBX (pRqBlk) No interruptions Return Rqb to pool. Not needed anymore

Chapter 18 Kernel Code H ; Allocate a link block MOV EAX, pFreeLB OR EAX, EAX JNZ Resp07 MOV EAX.ercNoMoreLBs JMP RespEnd NewLB <= pFreeLB; IF pFreeLB=NIL THEN No LBs; caller with error in the EAX register Resp07: MOV EBX,[EAX+NextLB] MOV pFreeLB, EBX DEC _nl_BLeft MOV DWORD PTR [EAX+LBType MOV DWORD PTR [EAX+NextLB MOV EBX, dRqHndl MOV [EAX+DataLo],EBX MOV EBX, dStatRet MOV [EAX+DataHi],EBX PUSH EAX CALL deQueueTSS OR EAX , EAX JNZ Resp08 POP EAX CALL enQueueMsg XOR EAX, EAX JMP SHORT RespEnd pFreeLB <= pFreeLB’ .Next Update stats ] , RESPLB ; This is a Response Link Block ], 0 ; pLB^.Next <= NIL; Get Request handle into EBX Store in lower half of pLB’.Data Get Status/Error into EBX Store in upper half of pLB’.Data Save pLB on the stack DeQueue a TSS on that Exch Did we get one? Yes, give up the message Get the pLB just saved EnQueue the Message on Exch No Error And get out! Resp08: POP EBX MOV [EAX+pLBRet],EBX CALL enQueueRdy MOV EAX.pRunTSS CALL enQueueRdy CALL deQueueRdy CMP EAX.pRunTSS JNE Resp10 XOR EAX, EAX JMP SHORT RespEnd Get the pLB just saved into EBX and put it in the TSS EnQueue the TSS on the RdyQ Get the Ptr To the Running TSS and put him on the RdyQ Get high priority TSS off the RdyQ If the high priority TSS is the same as the Running TSS then return Return to Caller with ere ok. Resp10: MOV pRunTSS.EAX MOV BX,[EAX+Tid] MOV TSS_Sel,BX INC _nSwitches MOV EAX, TimerTick MOV SwitchTick, EAX JMP FWORD PTR [TSS] XOR EAX, EAX Make the TSS in EAX the Running TSS Get the task Id (TR) Put it in the JumpAddr Save time of this switch for scheduler JMP TSS Return to Caller with ere ok. RespEnd: ST I MOV ESP, EBP POP EBP RETF 8 ; Rtn to Caller & Remove Params

0 Part TV ■ The Operating System Source Code MoveRequestQ This is the kernel MoveRequest primitive. The MoveRequest primitive allows a service to move a request to another exchange it owns. This cannot be used to forward a request to another service or job, because the pointers in the request block are not properly aliased. MoveRequest ( ) is very similar to SendMsg ( ) except that MoveRequest ( ) checks to ensure the destination exchange is owned by the sender. See Listing 18.14. Listing 18.14. MoveRequest kernel primitive code. Procedural Interface : MoveRequest(dRqBlkHndl, DestExch) :ercType dqMsg is the handle of the RqBlk to forward. DestExch the exchange to where the Request should be sent. dRqBlkHndl DestExch EQU [EBP+16] EQU [EBP+12] PUBLIC _MoveRequest: PUSH EBP MOV EBP, ESP MOV ESI, [EBP+12] CMP ESI.nExch JNAE MReq02 MOV EAX.ercOutOf Range JMP MReqEnd MReq02: MReq04: MOV EAX, ESI MOV EDX.sEXCH MUL EDX MOV EDX.prgExch ADD EAX.EDX MOV ESI.EAX MOV EDX, [EAX+Owner] CALL GetpCrntJCB CMP EDX, EAX JE MReq04 MOV EAX, ErcNotOwner JMP MReqEnd CLI ; Allocate a link block MOV EAX.pFreeLB OR EAX, EAX JNZ MReq08 MOV EAX.ercNoMoreLBs JMP MReqEnd Get Exchange Parameter in ESI Is the exchange out of range No, continue in the EAX register. Get out Exch => EAX Compute offset of Exch in rgExch Add offset of rgExch => EAX MAKE ESI <= pExch Put exch owner into EDX (pJCB) Leaves it in EAX (uses only EAX) If the exchange is not owned by sender return to the caller with error in the EAX register. Get out No interruptions from here on NewLB <■ pFreeLB; IF pFreeLB=NIL THEN No LBs; caller with error in the EAX register Go home with bad news

Chapter 18 Kernel Code H MReq08: MOV EBX,[EAX+NextLB] MOV pFreeLB, EBX DEC _nl_BLeft MOV DWORD PTR [EAX+LBType] MOV DWORD PTR [EAX+NextLB] MOV EBX, [EBP+16] MOV [EAX+DataL0],EBX MOV DWORD PTR [EAX+DataHi] PUSH EAX CALL deQueueTSS OR EAX, EAX JNZ MReq10 POP EAX CALL enQueueMsg JMP SHORT MReqEnd pFreeLB <= pFreeLB’ .Next Update stats REQLBA ; This is a Request Link Block Aliased 0 ; pLB’.Next <= NIL; ; RqHandle ; RqHandle into Lower 1/2 of Msg 0 ; Store zero in upper half of pLB’.Data Save pLB on the stack DeQueue a TSS on that Exch Did we get one? Yes, give up the message Get the pLB just saved EnQueue the Message on Exch And get out! MReq10: POP EBX MOV [EAX+pLBRet] CALL enQueueRdy MOV EAX.pRunTSS CALL enQueueRdy CALL deQueueRdy CMP EAX.pRunTSS JNE MReq12 XOR EAX, EAX JMP SHORT MReqEnd EBX Get the pLB just saved into EBX and put it in the TSS EnQueue the TSS on the RdyQ Get the Ptr To the Running TSS and put him on the RdyQ Get high priority TSS off the RdyQ If the high priority TSS is the same as the Running TSS then return Return to Caller with ere ok. MReq12: MOV pRunTSS.EAX MOV BX,[EAX+Tid] MOV TSS_Sel,BX INC _nSwitches MOV EAX, TimerTick MOV SwitchTick, EAX JMP FWORD PTR [TSS] XOR EAX, EAX Make the TSS in EAX the Running TSS Get the task Id (TR) Put it in the JumpAddr for Task Switch Keep track of how many switches for stats Save time of this switch for scheduler JMP TSS (This is the task switch) Return to Caller with ere ok. MReqEnd: ST I MOV ESP, EBP POP EBP RETF 8 SendMsgQ This is the kernel SendMsg primitive. It sends a nonspecific message from a running task to an exchange. The SendMsg primitive may cause a task switch, if a task is waiting at the exchange and it is of equal or higher priority than that of the task which sent the message. See Listing 18.15.

[336 Part PV ■ The Operating System Source Code Listing 18.15. SendMsg kernel primitive code. Procedural Interface : SendMsg(exch, dMsgl , dMsg2) :ercType exch is a DWORD (4 BYTES) containing the exchange to where the message should be sent. dMsgl & dMsg2 are DWord values defined and understood only by the sending and receiving tasks. SendExchange EQU [EBP+14h] MessageHi EQU DWORD PTR [EBP+10h] MessageLo EQU DWORD PTR [EBP+0Ch] PUBLIC _SendMsg: PUSH EBP MOV EBP, ESP MOV ESI, SendExchange CMP ESI.nExch JNAE Send00 MOV EAX.ercOutOf Range JMP SendEnd Get Exchange Parameter in ESI If the exchange is out of range the return to caller with error in the EAX register. Send00: MOV EAX, ESI MOV EDX.SEXCH MUL EDX MOV EDX.prgExch ADD EAX, EDX MOV ESI, EAX CMP DWORD PTR [EAX+Owner] JNE Send01 MOV EAX,ercNotAlloc JMP SendEnd Exch => EAX Compute offset of Exch in rgExch Add offset of rgExch => EAX MAKE ESI <= pExch , 0 ; If the exchange is not allocated return to the caller with error in the EAX register. Send0l CLI ; Allocate a link block MOV EAX.pFreeLB OR EAX, EAX JNZ SHORT Send02 MOV EAX.ercNoMoreLBs JMP SHORT MReqEnd No interrupts NewLB <= pFreeLB; IF pFreeLB=NIL THEN No LBs; caller with error in the EAX register Go home with bad news Send02: MOV EBX,[EAX+NextLB] MOV pFreeLB.EBX DEC nLBLeft pFreeLB <= pFreeLB” .Next Update stats MOV DWORD PTR [ EAX+LBType] , DATALB ; This is a Data Link Block MOV DWORD PTR [ EAX+NextLB] , 0 ; pLB’.Next <= NIL; MOV EBX.MessageLo ; Get lower half of Msg in EBX MOV [EAX+DataLo] ,EBX ; Store in lower half of pLB’.Data MOV EBX, MessageHi ; Get upper half of Msg in EBX MOV [EAX+DataHi) ,EBX ; Store in upper half of pLB’.Data

Chapter 18 ■ Kernel Code H Send25: Send03: Send04 EBX PUSH EAX CLI CALL deQueueTSS ST I OR EAX , EAX JNZ Send25 POP EAX CLI CALL enQueueMsg JMP Send04 POP EBX CLI MOV [EAX+pLBRet]; CALL enQueueRdy MOV EAX.pRunTSS CALL enQueueRdy CALL deQueueRdy CMP EAX.pRunTSS JNE Send03 JMP SHORT Send04 MOV pRunTSS.EAX MOV BX,[EAX+Tid] MOV TSS_Sel,BX INC _nSwitches MOV EAX, TimerTick MOV SwitchTick, EAX JMP FWORD PTR [TSS] XOR EAX EAX SendEnd ST I MOV ESP EBP POP EBP RETF 12 Save pLB on the stack No interrupts DeQueue a TSS on that Exch Did we get one? Yes, give up the message Get the pLB just saved No interrupts EnQueue the Message on Exch And get out (Ere 0) ! Get the pLB just saved into EBX No interrupts and put it in the TSS EnQueue the TSS on the RdyQ Get the Ptr To the Running TSS and put him on the RdyQ Get high priority TSS off the RdyQ If the high priority TSS is the same as the Running TSS then return Return with ErcOk Make the TSS in EAX the Running TSS Get the task Id (TR) Put it in the JumpAddr Save time of this switch for scheduler JMP TSS Return to Caller with ere ok. ISendMsgO This is the kernel ISendMsg primitive (Interrupt Send). This procedure allows an ISR to send a message to an exchange. This is the same as SendMsg ( ) except that no task switch is performed. If a task is waiting at the exchange, the message is associated (linked) with the task and then moved to the RdyQ. It will get a chance to run the next time the RdyQ is evaluated by the ker- nel, which will probably be caused by the timer interrupt slicer. Interrupt tasks can use ISendMsgO to send single or multiple messages to exchanges during their execution. Interrupts are clearedon entry and will not be set on exit! It is the responsibil- ity of the caller to set them, if desired. ISendMsg is intended to be used only by ISRs in device drivers.

358 | Part IV ■ The Operating System Source Code You may notice that jump instructions are avoided and code is placed in-line, where possible, for speed. See Listing 18.16. Listing 18.16. iSendMsg kernel primitive code. Procedural Interface : ISendMsg (exch, dMsgl , dMsg2) :ercType exch is a DWORD (4 BYTES) containing the exchange to where the message should be sent. dMsgl and dMsg2 are DWORD messages. Parameters on stack are the same as _SendMsg. PUBLIC _ISendMsg: CLI PUSH EBP MOV EBP, ESP MOV ESI.SendExchange CMP ESI.nExch JNAE ISend00 MOV EAX.ercOutOf Range MOV ESP, EBP POP EBP RETF 12 INTS ALWAYS CLEARED AND LEFT THAT WAY! Get Exchange Parameter in ESI If the exchange is out of range then return to caller with error in the EAX register. ISend00: MOV EAX, ESI MOV EDX.sEXCH MUL EDX MOV EDX.prgExch ADD EAX, EDX MOV ESI, EAX CMP DWORD PTR [EAX+Owner], 0 JNE ISend01 Exch => EAX Compute offset of Exch in rgExch MOV EAX.ercNotAlloc MOV ESP, EBP POP EBP RETF 12 Add offset of rgExch => EAX MAKE ESI <= pExch If the exchange is not allocated return to the caller with error in the EAX register. ; Allocate a link block MOV EAX, pFreeLB OR EAX, EAX JNZ SHORT ISend02 MOV EAX.ercNoMoreLBs MOV ESP, EBP POP EBP RETF 12 NewLB <= pFreeLB; IF pFreeLB=NIL THEN No LBs; caller with error in the EAX register ISend02: MOV EBX,[EAX+NextLB] MOV pFreeLB.EBX DEC nLBLeft pFreeLB <= pFreeLB* .Next

Chapter 18 Kernel Code B MOV DWORD PTR [EAX+LBType MOV DWORD PTR [EAX+NextLB MOV EBX.MessageLo MOV [EAX+DataLo],EBX MOV EBX.MessageHi MOV [EAX+DataHi],EBX PUSH EAX CALL deQueueTSS OR EAX , EAX JNZ ISend03 POP EAX CALL enQueueMsg JMP ISend04 ISend03: POP EBX MOV [EAX+pLBRet],EBX CALL enQueueRdy ISend04: XOR EAX, EAX MOV ESP, EBP POP EBP RETF 12 ],DATALB ; This is a Data Link Block ],0 ; pLB’.Next <= NIL; Get lower half of Msg in EBX Store in lower half of pLB’.Data Get upper half of Msg in EBX Store in upper half of pLB~.Data Save pLB on the stack DeQueue a TSS on that Exch Did we get one? Yes, give up the message No, Get the pLB just saved EnQueue the Message on Exch And get out! Get the pLB just saved into EBX and put it in the TSS EnQueue the TSS on the RdyQ Return to Caller with ere ok. WaitMsgO This is the kernel WaitMsg primitive. This procedure allows a task to receive information from another task via an exchange. If no message is at the exchange, the task is placed on the ex- change, and the ready queue is reevaluated to make the next highest-priority task run. The WaitMsg code is a very key piece to task scheduling. You will notice that if no task is ready to run, I simply halt the processor with interrupts enabled. A single flag named f Halted is set to let the interrupt slicer know that it doesn't have to check for a higher-priority task. If you halted, there wasn't one to begin with. The WaitMsgO and CheckMsg( ) primitives also have the job of aliasing memory addresses in- side of request blocks for system services. This is because they may be operating in completely different memory contexts, which means they have different page directories. See Listing 18.17. Listing 18.17. WaitMsg kernel primitive code A result code is returned in the EAX register. Procedural Interface : Wait(dExch.pdqMsgRet) :ercType dExch is a DWORD (4 BYTES) containing the exchange to

36o] Part PV ■ The Operating System Source Code Listing 18.17. continued where the message should be sent. pMessage is a pointer to an 8 byte area where the message is stored. WaitExchange EQU [EBP+10h] pMessage EQU [EBP+0Ch] PUBLIC _WaitMsg: PUSH EBP MOV EBP, ESP MOV ESI, WaitExchange CMP ESI.nExch JNAE Wait00 MOV EAX.ercOutOf Range MOV ESP, EBP POP EBP RETF 8 Get Exchange Parameter in ESI If the exchange is out of range ; the return to caller with error in the EAX register. Wait00: MOV EAX, ESI MOV EBX.sEXCH MUL EBX MOV EDX,prgExch ADD EAX.EDX MOV ESI, EAX CMP DWORD PTR [EAX+Owner] JNE Wait01 MOV EAX.ercNotAlloc MOV ESP, EBP POP EBP RETF 8 Exchld => EAX Compute offset of Exchld in rgExch Add offset of rgExch EAX Put Exch in to ESI , 0 ; If the exchange is not allocated return to the caller with error in the EAX register. Wait01 CLI CALL deQueueMsg OR EAX , EAX JZ Wait02 JMP Wait05 EAX <= pLB from pExch (ESI) If no message (pLB = NIL) Then Wait for Message Else Get Message and Return Wait02: MOV EAX.pRunTSS Get pRunTSS in EAX to Wait This next section of code Queues up the TSS pointed to by EAX on the exchange pointed to by ESI (i.e., we make the current task “wait”) MOV DWORD PTR [ EAX+NextTSS] , 0 pTSSin”. Next <= NIL; XCHG ESI, EAX pExch => EAX, pTSSin CMP DWORD PTR [EAX+EHead], 0 if ..TSSHead = NIL JNE Wait025 then MOV [EAX+EHead], ESI ; ..TSSHead <= pTSSin; MOV [EAX+ETail],ESI ; ..TSSl fail <= pTSSin; MOV DWORD PTR [EAX+fEMsg], 0 = > ESI Flag it as a TSS (vice a Msg)

Chapter 18 Kernel Code |
361 XCHG ESI, EAX JMP SHORT Wait03 Make ESI := pExch Again else Wait025: MOV EDX,[EAX+ETail] MOV [EDX+NextTSS],ESI MOV [EAX+ETail],ESI MOV DWORD PTR [EAX+fEMsg] XCHG ESI, EAX : TSSTail’.NextTSS <= pTSSin; . .TSSTail := pTSSin; Flag it as a TSS (vice a Msg) Make ESI <= pExch Again ;We just placed the current TSS on an exchange, ;now we get the next TSS to run (if there is one) Wait03: CALL deQueueRdy OR EAX, EAX JNZ Wait035 Get highest priority TSS off the RdyQ Anyone ready to run? Yes (jump to check pTSS) MOV EDI, 1 MOV df Halted, INC _nHalts ST I HLT CLI XOR EDI, EDI MOV df Halted, JMP Wait03 EDI No, then HLT CPU until ready Halt CPU and wait for interrupt An interrupt has occurred. Clear Interrupts Check for a task to switch to Wait035: CMP EAX.pRunTSS JE Wait04 Same one as before??? You bet! NO SWITCH! ! ! ;Now we switch tasks by placing the address of the ;new TSS in pRunTSS and jumping to it. This forces ;a hardware task switch. MOV pRunTSS.EAX MOV BX,[EAX+Tid] MOV TSS_Sel,BX INC _nSwitches MOV EAX, TimerTick MOV SwitchTick, EAX JMP FWORD PTR [TSS] Make high priority TSS Run. Save time of this switch for scheduler JUMP TSS (Switch Tasks) A task has just finished “Waiting” We are now in the new task with its memory space (or the same task if he was high pri & had a msg) If this is a system service it may need RqBlk address aliases If it is an OS service we alias in OS memory! Wait04: MOV EDX.pRunTSS MOV EAX,[EDX+pLBRet] Put the TSS in EAX into EDX Get the pLB in EAX Wait05: If we got here, we have either switched tasks and we are delivering a message (or Req) to the new task,

362 Part PV ■ The Operating System Source Code Listing 18.17. continued or there was a message waiting at the exch of the first caller and we are delivering it. Either way, the message is already deQueued from the exch and the critical part of WaitMsg is over. We can start interrupts again except when we have to return the Link Block to the pool (free it up) CMP DWORD PTR [EAX+LBType] ,REQLB JNE Wait06 WE CAN RESTART INTERRUPTS HERE Is the link block a Req Link Block? No, Treat it as a data link block ;pLB.DataLo is RqHandle (pRqBlk) PUSH EAX MOV EBX,[EAX+DataLo] ; Save ptr to Link Block ; Get pRqBlk into EBX Now we set up to alias the memory for the service (Alias the 2 Pointers in the RqBlk) _AliasMem(pMem, dcbMem, dJobNum, ppAliasRet): dError MOV ECX, [EBX+cbData1] OR ECX, ECX JZ Wait051 is cbDatal 0? Yes MOV EAX, [EBX+pData1] OR EAX, EAX JZ Wait051 is pDatal NULL”! Yes PUSH EAX PUSH ECX MOV EAX, PUSH EAX ADD EBX, PUSH EBX CALL FWORD PTR OR EAX, EAX JZ Wait051 POP EBX MOV ESP, EBP POP EBP RETF 8 [EBX+RqOwnerJob] pDatal AliasMem ;Set up params for AliasMem ;pMem ;cbMem ;dJobNum ; Offset to pDatal in RqBlk ; Linear Address of pDatal Error?? No, continue Make stack right Return Error. . . POP EAX PUSH EAX MOV EBX, [EAX+DataLo] ;Second Pointer (pData2) ;Restore ptr to Link Block ;Save again ; Get pRqBlk into EBX MOV ECX, [EBX+cbData2] OR ECX, ECX JZ Wait052 is cbData2 0? Yes MOV EAX, [EBX+pData2] OR EAX, EAX JZ Wait052 is pData2 NULL? Yes Set up params for AliasMem

Chapter 18 Kernel Code H PUSH EAX pMem PUSH ECX ;cbMem MOV EAX, [EBX+RqOwnerJob] PUSH EAX ;dJobNum ADD EBX, pData2 ;Offset to pData2 in RqBlk PUSH EBX ; Linear Address of PDatal CALL FWORD PTR _A1 LasMem OR EAX, EAX Error?? JZ Wait052 No, continue POP EBX Make stack right MOV ESP, EBP Return Error. . . POP EBP RETF 8 Wait052: POP EAX ; Restore ptr to Link Block Wait06: MOV EBX,[EAX+DataLo] MOV ECX,[EAX+DataHi] MOV EDX.pMessage MOV [EDX],EBX MOV [EDX+4],ECX ;Return the LB to the pool CLI MOV EBX.pFreeLB MOV [EAX+NextLB],EBX MOV pFreeLB, EAX INC _nLBLeft ST I XOR EAX, EAX MOV ESP, EBP POP EBP RETF 8 Get pLB^.Data into ECX:EBX Get Storage Addr in EDX Put pLB’.Data in specified memory space (EDX) pLBin’.Next <= pFreeLB; pFreeLB <= pLBin; ErcOK! (0) CheckMsgO This is the kernel CheckMsg primitive. This procedure allows a task to receive information from another task without blocking. In other words, if no message is available, CheckMsgO returns to the caller. If a message is available it is returned to the caller immediately. The caller is never placed on an exchange, and the ready queue is not evaluated. As with WaitMsg ( ) , address aliasing must also be accomplished here for system services if they have dif- ferent page directories. See Listing 18.18. Listing 18.18. CheckMsg kernel primitive code. A result code is returned in the EAX register.

os a I Part PV ■ The Operating System Source Code
Listing 18.18. continued
Procedureal Interface :
CheckMsg(exch,pdqMsg) :ercType
exch is a DWORD (4 BYTES) containing the exchange to where the
message should be sent.
pdqMsg is a pointer to an 8 byte area where the message is stored.
ChkExchange
pCkMessage
EQU [EBP+10h]
EQU [EBP+0Ch]
PUBLIC
Chk01
_CheckMsg:
PUSH EBP
MOV EBP, ESP
MOV ESI, ChkExchange
CMP ESI.nExch
JNAE Chk01
MOV EAX, ercOutOf Range
MOV ESP, EBP
POP EBP
RETF 8
MOV EAX, ESI
MOV EBX.sEXCH
MUL EBX
MOV EDX,prgExch
ADD EAX.EDX
MOV ESI, EAX
CMP DWORD PTR [EAX+Owner]
JNE Chk02
Get Exchange Parameter in ESI
If the exchange is out of range
then return to caller with error
in the EAX register.
Exch => EAX
Compute offset of Exch in rgExch
Add offset of rgExch => EAX
; Put pExch in to ESI
, 0 ; If the exchange is not allocated
return to the caller with error Chk02: MOV EAX.ercNotAlloc MOV ESP, EBP POP EBP RETF 8 CLI CALL deQueueMsg OR EAX , EAX JNZ Chk03 STI MOV EAX.ercNoMsg MOV ESP, EBP POP EBP RETF 8 STI in the EAX register. Can't be interrupted EAX <= pLB from pExch (ESI) If pLB = NIL Then Go to get msg and return return with ere no msg We can be interrupted again CMP DWORD PTR [EAX+LBType] ,REQLB ; Is the link block a Req Link Block? JNE Chk04 ; No, Treat it as a data link block ;pLB.DataLo is RqHandle (pRqBlk) PUSH EAX MOV EBX,[EAX+DataLo] Save ptr to Link Block Get pRqBlk into EBX

Chapter 18 ■ Kernel Code H Now we set up to alias the memory for the service (Alias the 2 Pointers in the RqBlk) _AliasMem(pMem, dcbMem, dJobNum, ppAliasRet) : dError Chk03l [EBX+RqOwnerJob] pDatal MOV ECX, [EBX+cbData1] OR ECX, ECX JZ Chk031 MOV EAX, [EBX+pData1] OR EAX, EAX JZ Chk031 PUSH EAX PUSH ECX MOV EAX, PUSH EAX ADD EBX, PUSH EBX CALL FWORD PTR _AliasMem OR EAX, EAX JZ Chk031 POP EBX MOV ESP, EBP POP EBP RETF 8 POP EAX PUSH EAX MOV EBX,[EAX+DataLo] MOV ECX, [EBX+cbData2] OR ECX, ECX JZ Chk032 MOV EAX, [EBX+pData2] OR EAX, EAX JZ Chk032 PUSH EAX PUSH ECX MOV EAX, [EBX+RqOwnerJob] PUSH EAX ADD EBX, pData2 PUSH EBX CALL FWORD PTR _AliasMem OR EAX, EAX JZ Chk032 POP EBX MOV ESP, EBP POP EBP RETF 8 is cbDatal 0? Yes is pDatal NULL? Yes Set up params for AliasMem pMem cbMem dJobNum Offset to pDatal in RqBlk Linear Address of pDatal Error?? No, continue Make stack right Return Error. . . Second Pointer (pData2) Restore ptr to Link Block Save again Get pRqBlk into EBX is cbData2 0? Yes is pData2 NULL? Yes Set up params for AliasMem pMem cbMem dJobNum Offset to pData2 in RqBlk Linear Address of PDatal Error?? No, continue Make stack right Return Error. . . Chk032: POP EAX :Restore Ptr to Link Block

[366 Part PV ■ The Operating System Source Code Listing 18.18. continued Chk04: MOV EBX,[EAX+DataL0] MOV ECX,[EAX+DataHi] MOV EDX.pCkMessage MOV [EDX],EBX MOV [EDX+4],ECX ; Return the LB to the pool CLI MOV EBX, pFreeLB MOV [EAX+NextLB],EBX MOV pFreeLB, EAX INC _nLBLeft ST I XOR EAX, EAX MOV ESP, EBP POP EBP RETF 8 Get pLBA.Data into ECX:EBX Get Storage Addr in EDX Put pLB~.Data in specified memory space (EDX) pLBin~.Next <= pFreeLB; pFreeLB <= pLBin; ErcOK! (0) NewTaskQ This is the kernel NewTask ( ) primitive. This creates a new task and schedules it for execution. This primitive is used primarily to create a task for a job other than the one you are in. The operating system job-management code uses this to create the initial task for a newly loaded job. For MMURTL version 1 .x, stack protection is not provided for transitions through the call gates to the OS code. In future versions, NewTask( ) will allocate operating system stack space separately from the stack memory the caller provides as a parameter. The protection in this version of MMURTL is limited to page protection, and any other access violations caused will result in a general protection violation or page fault. The change will be transparent to appli- cations when it occurs. See Listing 18.19. Listing 18.19. NewTask kernel primitive code. Procedural interface: NewTask (JobNum, CodeSeg, Priority, fDebug, Exch, ESP, EIP) : dErcRet NTS Job EQU [EBP+36] ;Job Num for this task NTS CS EQU [EBP+32] ;8 for OS, 18h for user task NTS Pri EQU [EBP+28] ;Priority of this task NTS fDbg EQU [EBP+24] ;TRUE for DEBUGing NTS Exch EQU [EBP+20] ; Exchange for TSS NTS ESP EQU [EBP+16] ; Initial stack pointer NTS EIP EQU [EBP+12] ;Task start address

Chapter 18 ■ Kernel Code PUBLIC _NewTask: PUSH EBP MOV EBP, ESP MOV EDX, NTS_Pri CMP EDX, nPRI-1 JBE NT0000 MOV EAX.ercBadPriority JMP NTEnd ; Priority OK? NT0000: MOV ECX, NTS_Exch CMP ECX, nExch JBE NT0001 MOV EAX.ercOutOf Range JMP NTEnd ;Exch in range? NT0001 CLI MOV EAX.pFreeTSS OR EAX, EAX JNZ NT0002 MOV EAX.ercNoMoreTSSs JMP NTEnd ;we can't be interrupted ; NewTSS <= pFreeTSS; ; IF pFreeTSS=NIL THEN Return; •No... NT0002: MOV EBX,[EAX+NextTSS] MOV pFreeTSS, EBX DEC _nTSSLeft STI pFreeTSS <= pFreeTSSA .Next ;EAX now has pNewTSS MOV [EAX+Priority],DL ;put Priority into TSS MOV DWORD PTR [EAX+TSS_EFlags] ,0202h ;Load the Flags Register ECX MOV [EAX+TSS_Exch] MOV EBX, NTS_EIP MOV [EAX+TSS_EIP],EBX MOV EBX, NTS_ESP MOV [EAX+TSS_ESP],EBX MOV [EAX+TSS_ESP0],EBX MOV ECX, NTS_CS MOV [EAX+TSS_CS],CX ;Put new Exch in TSS (ECX is free) ;mov EIP into TSS (Start Address) ;mov ESP into TSS ;mov CS into TSS PUSH EAX ;Save pNewTSS ;Now we get pJCB from JobNum they passed in so we can ;get the PD from the JCB MOV EAX, NTS_J0b CALL GetpJCB MOV ECX, EAX ;Set up to call GetpJCB ;EAX now has pJCB ;ECX now has pJCB POP EAX ; Restore pNewTSS to EAX MOV [EAX+TSS_pJCB],ECX MOV EBX, [ECX+JcbPD] ;Put pJCB into TSS ;Set up to call LinToPhy

Part TV ■ The Operating System Source Code Listing 18.19. continued NT0004: PUSH EAX MOV EAX, NTS_Job CALL LinToPhy MOV EBX, EAX POP EAX MOV [EAX+TSS_CR3],EBX CMP DWORD PTR NTS_fDbg, JE NT0004 ;Save pNewTSS again ;Get Physical Address for PD into EAX ;pNewTSS into EAX ;Put Physical Add for PD into TSSCR3 ;Debug on entry? ;No MOV WORD PTR [EAX+TSS_TrapBit] , 1 ;Yes MOV EBX, NTS_Pri CLI MOV EDX.pRunTSS CMP BYTE PTR [EDX+Priority] JA NT0005 CALL enQueueRdy XOR EAX, EAX JMP NTEnd ;Get priority of new task ;We can't be interrupted ;6et who's running BL ;Who got the highest Pri? ;New guy does (lowest num) ;Just put new guy on the ReadyQue (EAX) ;ercOk ;Return to caller NT0005: NTEnd: XCHG EAX.EDX PUSH EDX CALL enQueueRdy POP EAX MOV pRunTSS.EAX MOV BX,[EAX+Tid] MOV TSS_Sel,BX INC _nSwitches MOV EAX, TimerTick MOV SwitchTick, EAX JMP FWORD PTR [TSS] XOR EAX, EAX STI MOV ESP, EBP POP EBP RETF 28 CrntTSS -> EAX, New TSS Save New TSS EDX New TSS -> EAX Move new TSS into pRunTSS Put Selector/Offset in “TSS” Save time of this switch for scheduler ;Jump to new TSS ;ErcOk SpawnTaskQ The SpawnTask is the kernel primitive used to create another task (a thread) in the context of the caller's job control block and memory space. It's the easy way to create additional tasks for existing jobs. As with NewTask ( ) , the newly created task is placed on the ready queue if it is not a higher pri- ority than the task that created it. See Listing 18.20.

Chapter 18 ■ Kernel Code H Listing 18.20. SpawnTask kernel primitive code. Procedural Interface: SpawnTask (pEntry, dPriority, fDebug, pStack, fOSCode); pEntryST EQU DWORD PTR [EBP+28] dPriST EQU DWORD PTR [EBP+24] fDebugST EQU DWORD PTR [EBP+20] pStackST EQU DWORD PTR [EBP+16] fOSCodeST EQU DWORD PTR [EBP+12] NewExchST NewTSSST EQU DWORD PTR [EBP -4] EQU DWORD PTR [EBP -8] PUBLIC SpawnTask: PUSH EBP MOV EBP, ESP SUB ESP, 8 CMP dPriST, nPRI-1 JBE ST0001 MOV EAX.ercBadPriority JMP STEnd two local DWORD vars Priority OK? ST0001 LEA EAX, NewExchST PUSH EAX CALL FWORD PTR _AllocExch OR EAX, EAX JNZ STEnd ;Allocate exchange ;see if we got an error ;Yup, bad news ;Allocate a new TSS CLI MOV EAX.pFreeTSS OR EAX, EAX JNZ ST0002 ST I we can't be interrupted NewTSS <= pFreeTSS; IF pFreeTSS=NIL THEN Return; ;Dealloc Exch if we didn't get a TSS PUSH NewExchST CALL FWORD PTR _DeAllocExch MOV EAX.ercNoMoreTSSs ;No... JMP NTEnd ST0002: MOV EBX,[EAX+NextTSS] MOV pFreeTSS, EBX DEC _nTSSLeft ST I pFreeTSS <= pFreeTSS’ .Next MOV NewTSSST, EAX MOV EBX, NewExchST MOV [EAX+TSS_Exch],EBX MOV WORD PTR [EAX+TSS_CS] CMP fOSCodeST, 0 JNE ST0003 MOV WORD PTR [EAX+TSS_CS] ;Save new TSS ;mov exch into TSS OSCodeSel [Defaults to OS code selector JobCodeSel ;Make OS code selector

370 I Part W M The Operating System Source Code Listing 18.20. continued ST0003: MOV EBX.pEntryST MOV [EAX+TSS_EIP],EBX MOV EBX, pStackST MOV [EAX+TSS_ESP],EBX MOV [EAX+TSS_ESP0],EBX MOV EBX, pRunTSS MOV EDX, [EBX+TSS_pJCB] MOV [EAX+TSS_pJCB],EDX MOV EDX, [£BX+TSS_CR3] MOV [EAX+TSS_CR3],EDX ;mov EIP into TSS ;mov ESP into TSS ;Get pJCB from Crnt Task ;Get CR3 from crnt task ; move into new TSS MOV DWORD PTR [EAX+TSS_EFlags] ,0202h ;Load the Flags Register CMP fDebugST, 0 ; Debug on entry? JE ST0004 ;No MOV WORD PTR [EAX+TSS_TrapBit] , 1 ;Yes ST0004: ST0005: STEnd: MOV EBX, dPriST MOV [EAX+Priority],BL CLI MOV EDX.pRunTSS CMP [EDX+Priority],BL JA ST0005 CALL enQueueRdy XOR EAX, EAX JMP STEnd XCHG EAX, EDX PUSH EDX CALL enQueueRdy POP EAX MOV pRunTSS.EAX MOV BX,[EAX+Tid] MOV TSS_Sel,BX INC _nSwitches MOV EAX, TimerTick MOV SwitchTick, EAX JMP FWORD PTR [TSS] XOR EAX, EAX ST I MOV ESP, EBP POP EBP RETF 20 ;mov priority into BL ;put in TSS ;we can't be interrupted ;Get who's running ;Who got the highest Pri? ;If crnt >, New guy does (lowest num) ;01d guy does, just put new guy on Q. ;ercOk :Return to caller ;CrntTSS -> EAX, New TSS -> EDX ;New TSS -> Stack ; Place crnt TSS on Q ;New TSS -> EAX ;Move new TSS into pRunTSS ;Put Selector/Offset in ■TSS” Save time of this switch for scheduler ;Jump to new TSS ;ErcOk AllocExchQ The AllocExch( ) is an auxiliary function to allocate an exchange. The exchange is a “message port” where you send and receive messages. It is a property of a job (not a task). See Listing 18.21.

Chapter 18 ■ Kernel Code H Listing 18.21. Allocate exchange code. Procedural Interface : AllocExch(pExchRet) :dError pExchRet is a pointer to where you want the Exchange Handle returned. The Exchange Handle is a DWORD (4 BYTES). PUBLIC AE000: AE001 _AllocExch: PUSH EBP MOV EBP, ESP XOR ESI, ESI MOV EBX.prgExch MOV ECX.nExch Zero the Exch Index EBX <= ADR rgExch Get number of exchanges in ECX CLI CMP DWORD PTR [EBX+Owner], 0 JE AE001 ADD EBX.sEXCH INC ESI LOOP AE000 STI MOV EAX.ercNoMoreExch MOV ESP, EBP POP EBP RETF 4 Is this exchange free to use If we found a Free Exch, JUMP Point to the next Exchange Increment the Exchange Index Keep looping until we are done There are no more free exchanges MOV EDX,[EBP+0CH] MOV [EDX],ESI MOV EDX.pRunTSS MOV EAX,[EDX+TSS_pJCB] MOV [EBX+Owner],EAX STI MOV DWORD PTR [EBX+EHead] ,0 MOV DWORD PTR [EBX+ETail] ,0 DEC nEXCHLeft Get the pExchRet in EDX Put Index of Exch at pExchRet Get pRunTSS in EDX Get the pJCB in EAX Make the Exch owner the Job Make the msg/TSS queue NIL Stats XOR EAX, EAX ercOK (0) MOV ESP, EBP POP EBP RETF 4 DeAllocExchO The DeAllocExchO function is the compliment of AllocExch( ). The DeAllocExch( ) function has a lot more to do, however. When an exchange is returned to the free pool of exchanges, it may have left over link blocks hanging on it. Those link blocks may also contain requests. This means that DeAllocExchO must also be a garbage collector.

372 | Part TV ■ The Operating System Source Code Messages are dequeued, and link blocks, TSSs, and request blocks are freed up, as necessary. See Listing 18.22. Listing 18.22. Deallocate exchange code. Procedural Interface : DeAllocExch(Exch) :ercType Exch is the Exchange Handle the process is asking to be released. PUBLIC _DeAllocExch: ; PUSH EBP ; MOV EBP, ESP : MOV ESI,[EBP+0CH] MOV EAX,ESI MOV EDX.sEXCH MUL EDX MOV EDX.prgExch ADD EAX, EDX MOV ECX, EAX MOV EDX.pRunTSS MOV EBX,[EDX+TSS_pJCB] MOV EDX, [EAX+Owner] CMP EBX.EDX JE DE000 CMP EBX, OFFSET MonJCB JE DE000 MOV EAX.ercNotOwner MOV ESP, EBP POP EBP RETF 4 Load the Exchange Index in ESI Get the Exchange Index in EAX Compute offset of Exch in rgExch Add offset of rgExch => EAX Make a copy in ECX (ECX = pExch) Get the pRunTSS in EDX Get pJCB in EBX Get the Exchange Owner in EDX If the CurrProc owns the Exchange, yes if not owner, is this the OS??? yes DE000: CLI ; CMP DWORD PTR [ECX+f EMsg] ,0 ; JE DE001 ; MOV ESI, ECX ; CALL deQueueMsg ; Yes OR EAX, EAX See if a message may be queued No. Go check for Task (TSS) ESI must point to Exch for deQueue Get the message off of the Exchange JZ DE002 Nothing there. Go free the Exch, ;Return the LB to the pool MOV EBX, pFreeLB MOV [EAX+NextLB],EBX MOV pFreeLB.EAX INC _nLBLeft JMP DE000 pLBin’.Next <= pFreeLB; pFreeLB <= pLBin; Go And Check for more. ; If we find an RqBlk on the exchange we must respond ;with ErcInvalidExch before we continuel This will ;only happen if a system service writer doesn't follow Instructions or a service crashes!

Chapter 18 ■ Kernel Code 0 DE001 CMP DWORD PTR [ECX+EHead] , 0 Check to See if TSS is queued JE DE002 MOV ESI, ECX CALL deQueueTSS NIL = Empty, JUMP ESI must point to Exch for deQueue Get the TSS off of the Exchange ;Free up the TSS (add it to the free list) MOV EBX.pFreeTSS j pTSSin^.Next <= pFreeTSS; MOV [EAX+NextTSS],EBX MOV DWORD PTR [EAX+TSS_pJCB] , 0 MOV pFreeTSS, EAX INC _nTSSLeft ; JMP DE001 : Go And Check for more Make TSS invalid pFreeTSS <= pTSSin; DE002: MOV DWORD PTR [ECX+Owner], 0 MOV DWORD PTR [ECX+fEMsg], 0 Free up the exchange. Reset msg Flag. INC nEXCHLeft STI XOR EAX EAX MOV ESP EBP POP EBP RETF 4 Stats ercOK (0) GetTSSExchQ The GetTSSExch ( ) function is an auxiliary function that returns the exchange of the current TSS to the caller. This is primarily provided for system services that provide direct access blocking calls for customers. These services use the default exchange in the TSS to make a request for the caller of a procedural interface. See Listing 18.23. Listing 18.23. Get TSS exchange code. Procedural Interface GetTSSExch (pExchRet) :dError pExchRet is a pointer to where you want the Exchange Handle returned. The Exchange is a DWORD (4 BYTES). PUBLIC GetTSSExch: PUSH EBP MOV EBP, ESP MOV EAX.pRunTSS MOV ESI,[EBP+0CH] MOV EBX, [EAX+TSS_Exch] MOV [ESI], EBX XOR EAX, EAX POP EBP Get the Ptr To the Running TSS Get the pExchRet in EDX Get Exch in EBX Put Index of Exch at pExchRet ErcOK RETF 4

0 Part IV The Operating System Source Code SetPriorityO The auxiliary function SetPriorityO sets the priority of the caller's task. This doesn't really affect the kernel, because the new priority isn't used until the task is rescheduled for execution. If this is used to set a temporary increase in priority, the complimentary call GetPriority( ) should be used so that the task can be returned to its original priority. See Listing 18.24. Listing 18.24. Set priority code. SetPriority - This sets the priority of the task that called it to the priority specified in the single parameter. Procedural Interface : SetPriority(bPriority) :dError bPriority is a byte with the new priority. PUBLIC _SetPriority ; PUSH EBP ; MOV EBP, ESP ; MOV EAX.pRunTSS ; Get the Ptr To the Running TSS MOV EBX,[EBP+0CH] ; Get the new pri into EBX AND EBX, 01Fh ; Nothing higher than 31! MOV BYTE PTR [EAX+Priority] , BL ;Put it in the TSS XOR EAX, EAX ; ErcOK - No error. POP EBP ; RETF 4 : GetPriorityO The GetPriorityO function is an auxiliary function that returns the current priority of the running task. Callers should use GetPriority ( ) before using the SetPriority ( ) call so that they can return to their original priority when their “emergency” is over. See Listing 18.25. Listing 18.25. Get priority code. GetPriority - This gets the priority of the task that called it and passes it to bPriorityRet. Procedural Interface : GetPriority (bPriorityRet) :dError bPriorityret is a pointer to a byte where you want the priority returned.

Chapter 18 Kernel Code 0 PUBLIC _GetPriority PUSH EBP MOV EBP, ESP MOV EAX.pRunTSS MOV EBX,[EBP+0CH] MOV DL, BYTE PTR [EAX+Priority] MOV BYTE PTR [EBX], DL XOR EAX, EAX POP EBP RETF 4 Get the Ptr To the Running TSS Get the return pointer into EBX ErcOK - No error.

Powered by TurnKey Linux.