--- /dev/null
+; MMURTL Operating System Source Code\r
+; Copyright 1991,1992,1993,1994 Richard A. Burgess\r
+; ALL RIGHTS RESERVED\r
+; Version 1.0\r
+\r
+.DATA\r
+.INCLUDE MOSEDF.INC\r
+.INCLUDE TSS.INC\r
+.INCLUDE RQB.INC\r
+.INCLUDE JOB.INC\r
+\r
+dJunk DD 0 ;Used as temp in Service Abort function\r
+dmsg DD 2 DUP(0) ;tmp for abort\r
+\r
+EXTRN TimerTick DD\r
+EXTRN SwitchTick DD\r
+EXTRN dfHalted DD\r
+EXTRN _nSwitches DD\r
+EXTRN _nHalts DD\r
+EXTRN _nReady DD\r
+\r
+.CODE\r
+\r
+EXTRN LinToPhy NEAR\r
+\r
+;This file contains all the internal kernel functions plus\r
+;the PUBLIC kernel functions SendMsg, ISendMsg, WaitMsg, CheckMsg,\r
+;Request, Respond, MoveMsg, NewTask and SpawnTask.\r
+;Exchange management functions such as AllocExch and DeAllocExch are\r
+;also here.\r
+;\r
+; Note on interrupts and the kernel primitives:\r
+; Because certain kernel functions may be called from ISRs,\r
+; and because portions of other kernel functions may be\r
+; interrupted by a task change that happens because of an action\r
+; that an ISR takes, we must ensure that interrupts are\r
+; DISABLED prior to the allocation or deallocation\r
+; of ALL kernel data segment resources. This especially applies when\r
+; a message is "in transit." For example: taken from an exchange\r
+; but not yet linked to a TSS and placed on the ready queue.\r
+; This is important!\r
+;\r
+; NOTE on Exchanges, Messages, and Tasks (TSSs)\r
+; In MMURTL, an exchange is a place where either Messages or\r
+; Tasks wait. There can never be tasks AND messages at an\r
+; exchange at the same time (unless the kernel is BROKEN!).\r
+; When a message is sent to an exchange, if a task is waiting\r
+; there, is it immediately associated with the message\r
+; and placed on the readyQ in priority order.\r
+; For this reason we share the HEAD and TAIL link pointers\r
+; for tasks and messages on an exchange.\r
+;\r
+;=============================================================================\r
+\r
+enQueueMsg:\r
+;\r
+; INPUT : ESI,EAX\r
+; OUTPUT : NONE\r
+; REGISTERS : EAX,EDX,ESI,FLAGS\r
+; MODIFIES : EDX\r
+;\r
+; This routine will place the link block pointed to by EAX onto the exchange\r
+; pointed to by the ESI register. If EAX is NIL then the routine returns.\r
+;\r
+ OR EAX,EAX ; if pLBin = NIL THEN Return;\r
+ JZ eqMsgDone ;\r
+ MOV DWORD PTR [EAX+NextLB], 0 ; pLBin^.Next <= NIL;\r
+ XCHG ESI,EAX ; pExch => EAX, pLBin => ESI\r
+ CMP DWORD PTR [EAX+EHead], 0 ; if ..MsgHead = NIL\r
+ JNE eqMNotNIL ; then\r
+ MOV [EAX+EHead],ESI ; ..MsgHead <= pLBin;\r
+ MOV [EAX+ETail],ESI ; ..MsgTail <= pLBin;\r
+ MOV DWORD PTR [EAX+fEMsg], 1 ; Flag it as a Msg (vice a task)\r
+ XCHG EAX,ESI ; Put pExch Back in ESI\r
+ RETN ; else\r
+eqMNotNIL:\r
+ MOV EDX,[EAX+ETail] ; ..MsgTail^.NextLB <= pLBin;\r
+ MOV [EDX+NextLB],ESI ;\r
+ MOV [EAX+ETail],ESI ; ..MsgTail <= pLBin;\r
+ MOV DWORD PTR [EAX+fEMsg], 1 ; Flag it as a Msg (vice a task)\r
+ XCHG EAX,ESI ; Put pExch Back in ESI\r
+eqMsgDone:\r
+ RETN ;\r
+\r
+;=============================================================================\r
+\r
+deQueueMsg:\r
+;\r
+; INPUT : ESI\r
+; OUTPUT : EAX\r
+; REGISTERS : EAX,EBX,ESI,FLAGS\r
+; MODIFIES : *prgExch[ESI].msg.head and EBX\r
+;\r
+; This routine will dequeue a link block on the exchange pointed to by the\r
+; ESI register and place the pointer to the link block dequeued into EAX.\r
+;\r
+ MOV EAX,[ESI+fEMsg] ; Get Msg Flag\r
+ OR EAX, EAX ; Is it a Msg?\r
+ JZ deMsgDone ; No! (return 0)\r
+ MOV EAX,[ESI+EHead] ; pLBout <= ..MsgHead;\r
+ OR EAX, EAX ; if pLBout = NIL then Return;\r
+ JZ deMsgDone ;\r
+ MOV EBX,[EAX+NextLB] ; ..MsgHead <= ..MsgHead^.Next;\r
+ MOV [ESI+EHead],EBX ;\r
+deMsgDone:\r
+ RETN ;\r
+\r
+;=============================================================================\r
+\r
+deQueueTSS:\r
+;\r
+; INPUT : ESI\r
+; OUTPUT : EAX\r
+; REGISTERS : EAX,EBX,ESI,FLAGS\r
+; MODIFIES : EAX,EBX\r
+;\r
+; This routine will dequeue a TSS on the exchange pointed to by the ESI\r
+; register and place the pointer to the TSS dequeued into EAX.\r
+; EAX return NIL if no TSS is waiting at Exch ESI\r
+;\r
+ XOR EAX,EAX ; Set up to return nothing\r
+ MOV EBX,[ESI+fEMsg] ; Msg flag (is it a Msg)\r
+ OR EBX, EBX\r
+ JNZ deTSSDone ; It's a Msg (return leaving EAX 0)\r
+ MOV EAX,[ESI+EHead] ; pTSSout <= ..TSSHead;\r
+ OR EAX, EAX ; if pTSSout = NIL then Return;\r
+ JZ deTSSDone ;\r
+ MOV EBX,[EAX+NextTSS] ; ..TSSHead <= ..TSSHead^.Next;\r
+ MOV [ESI+EHead],EBX ;\r
+deTSSDone:\r
+ RETN ;\r
+\r
+;=============================================================================\r
+\r
+PUBLIC enQueueRdy:\r
+;\r
+; INPUT : EAX\r
+; OUTPUT : NONE\r
+; REGISTERS : EAX,EBX,EDX,FLAGS\r
+; MODIFIES : EAX,EBX,EDX\r
+;\r
+; This routine will place a TSS pointed to by EAX onto the ReadyQueue. This\r
+; algorithm chooses the proper priority queue based on the TSS priority.\r
+; The Rdy Queue is an array of QUEUES (2 pointers, head & tail per QUEUE).\r
+; This links the TSS to rgQueue[nPRI].\r
+;\r
+ OR EAX,EAX ; if pTSS = NIL then return;\r
+ JZ eqRdyDone ;\r
+ INC _nReady ;\r
+ MOV DWORD PTR [EAX+NextTSS], 0 ; pTSSin^.Next <= NIL;\r
+ XOR EBX,EBX ; get the priority\r
+ MOV BL,[EAX+Priority] ; in EBX\r
+ XCHG EAX,EBX ; Priority => EAX, pTSSin => EBX\r
+ SHL EAX, 3 ; Times 8 (size of QUEUE)\r
+ LEA EDX,RdyQ ; Add offset of RdyQ => EAX\r
+ ADD EAX,EDX ; EAX pts to proper Rdy Queue\r
+ CMP DWORD PTR [EAX+Head], 0 ; if Head = NIL\r
+ JNE eqRNotNIL ; then\r
+ MOV [EAX+Head],EBX ; ..Head <= pTSSin;\r
+ MOV [EAX+Tail],EBX ; ..Tail <= pTSSin;\r
+ RETN ; else\r
+eqRNotNIL:\r
+ MOV EDX,[EAX+Tail] ; ..Tail^.NextTSS <= pTSSin;\r
+ MOV [EDX+NextTSS],EBX ;\r
+ MOV [EAX+Tail],EBX ; ..Tail <= pTSSin;\r
+eqRdyDone:\r
+ RETN ;\r
+\r
+;=============================================================================\r
+\r
+PUBLIC deQueueRdy:\r
+;\r
+; INPUT : NONE\r
+; OUTPUT : EAX\r
+; REGISTERS : EAX,EBX,ECX,FLAGS\r
+; MODIFIES : RdyQ\r
+;\r
+; This routine will return a pointer in EAX to the highest priority task\r
+; queued on the RdyQ. Then the routine will "pop" the TSS from the RdyQ.\r
+; If there was no task queued, EAX is returned as NIL.\r
+;\r
+ MOV ECX,nPRI ; Set up the number of times to loop\r
+ LEA EBX,RdyQ ; Get base address of RdyQ in EBX\r
+deRdyLoop:\r
+ MOV EAX,[EBX] ; Get pTSSout in EAX\r
+ OR EAX, EAX ; IF pTSSout is NIL Then go and\r
+ JNZ deRdyFound ; check the next priority.\r
+ ADD EBX,sQUEUE ; Point to the next Priority Queue\r
+ LOOP deRdyLoop ; DEC ECX and LOOP IF NOT ZERO\r
+deRdyFound:\r
+ OR EAX, EAX ; IF pTSSout is NIL Then there are\r
+ JZ deRdyDone ; No TSSs on the RdyQ; RETURN\r
+ DEC _nReady ;\r
+ MOV ECX,[EAX+NextTSS] ; Otherwise, deQueue the process\r
+ MOV [EBX],ECX ; And return with the pointer in EAX\r
+deRdyDone:\r
+ RETN ;\r
+\r
+;=============================================================================\r
+\r
+PUBLIC ChkRdyQ:\r
+;\r
+; INPUT : NONE\r
+; OUTPUT : EAX\r
+; REGISTERS : EAX,EBX,ECX,FLAGS\r
+; MODIFIES : RdyQ\r
+;\r
+; This routine will return a pointer to the highest priority TSS that\r
+; is queued to run. It WILL NOT remove it from the Queue.\r
+; If there was no task queued, EAX is returned as NIL.\r
+;\r
+ MOV ECX,nPRI ; Set up the number of times to loop\r
+ LEA EBX,RdyQ ; Get base address of RdyQ in EBX\r
+ChkRdyLoop:\r
+ MOV EAX,[EBX] ; Get pTSSout in EAX\r
+ OR EAX, EAX ; IF pTSSout is NIL Then go and\r
+ JNZ ChkRdyDone ; check the next priority.\r
+ ADD EBX,sQUEUE ; Point to the next Priority Queue\r
+ LOOP ChkRdyLoop ; DEC ECX and LOOP IF NOT ZERO\r
+ChkRdyDone:\r
+ RETN ;\r
+\r
+;=============================================================================\r
+;================= BEGIN NEAR KERNEL HELPER ROUTINES =========================\r
+;=============================================================================\r
+\r
+; RemoveRdyJob (NEAR)\r
+;\r
+; This routine searchs all ready queue priorities for tasks belonging\r
+; to pJCB. When one is found it is removed from the queue\r
+; and the TSS is freed up. This is called when we are killing\r
+; a job.\r
+;\r
+; Procedureal Interface :\r
+;\r
+; RemoveRdyJob(char *pJCB):ercType\r
+;\r
+; pJCB is a pointer to the JCB that the tasks to kill belong to.\r
+;\r
+; pJCB EQU DWORD PTR [EBP+8]\r
+;\r
+; INPUT : (pJCB on stack)\r
+; OUTPUT : NONE\r
+; REGISTERS : All general registers are trashed\r
+; MODIFIES : RdyQ\r
+;\r
+;\r
+PUBLIC _RemoveRdyJob:\r
+;\r
+ PUSH EBP ;\r
+ MOV EBP,ESP ;\r
+ MOV ECX,nPRI ; Set up the number of times to loop\r
+ LEA EBX,RdyQ ; Get base address of RdyQ in EBX\r
+ MOV EDX, [EBP+8] ; EDX holds pJCB for comparison (RAB)\r
+\r
+ ;EBX points to begining of next Priority Queue\r
+RemRdyLoop:\r
+ MOV EAX,[EBX+Head] ; Get pTSS in EAX\r
+ MOV EDI, EAX ; EDI points to last TSS by default (or NIL)\r
+ OR EAX,EAX ; Is pTSS 0 (none left queued here)\r
+ JNZ RemRdy0 ; Valid pTSS!\r
+RemRdyLoop1:\r
+ MOV [EBX+Tail], EDI ; EDI always points to last TSS or NIL\r
+ ADD EBX,sQUEUE ; Point to the next Priority Queue\r
+ LOOP RemRdyLoop ; DEC ECX and LOOP IF NOT ZERO\r
+\r
+ XOR EAX, EAX ; No error\r
+ POP EBP\r
+ RETN 4 ; All done (clean stack)\r
+\r
+ ;Go here to dequeue a TSS at head of list\r
+RemRdy0:\r
+ CMP EDX, [EAX+TSS_pJCB] ; Is this from the JCB we want?\r
+ JNE RemRdy2 ; No\r
+\r
+ MOV EDI, [EAX+NextTSS] ; Yes, deQueue the TSS\r
+ MOV [EBX+Head], EDI ; Fix link in Queue list\r
+\r
+ PUSH EBX ; Save ptr to RdyQue (crnt priority)\r
+\r
+ ;Free up the TSS (add it to the free list)\r
+ MOV EBX,pFreeTSS ; pTSSin^.Next <= pFreeTSS;\r
+ MOV [EAX+NextTSS],EBX ;\r
+ MOV DWORD PTR [EAX+TSS_pJCB], 0 ; Make TSS invalid\r
+ MOV pFreeTSS,EAX ; pFreeTSS <= pTSSin;\r
+ INC _nTSSLeft ;\r
+\r
+ POP EBX\r
+ MOV EAX, EDI ; Make EAX point to new head TSS\r
+ OR EAX, EAX ; Is it Zero?\r
+ JZ RemRdyLoop1 ; Next Queue please\r
+ JMP RemRdy0 ; back to check next at head of list\r
+\r
+ ;Go here to dequeue a TSS in middle or end of list\r
+RemRdy2:\r
+ MOV EAX, [EDI+NextTSS] ; Get next link in list\r
+ OR EAX, EAX ; Valid pTSS?\r
+ JZ RemRdyLoop1 ; No. Next Queue please\r
+ CMP EDX, [EAX+TSS_pJCB] ; Is this from JCB we want?\r
+ JE RemRdy3 ; Yes. Trash it.\r
+ MOV EDI, EAX ; No. Next TSS\r
+ JMP RemRdy2\r
+RemRdy3:\r
+ ;EDI points to prev TSS\r
+ ;EAX points to crnt TSS\r
+ ;Make ESI point to NextTSS\r
+\r
+ MOV ESI, [EAX+NextTSS] ; Yes, deQueue the TSS\r
+\r
+ ;Now we fix the list (Make Prev point to Next)\r
+ ;This extracts EAX from the list\r
+\r
+ MOV [EDI+NextTSS], ESI ;Jump the removed link\r
+ PUSH EBX ;Save ptr to RdyQue (crnt priority)\r
+\r
+ ;Free up the TSS (add it to the free list)\r
+ MOV EBX,pFreeTSS ; pTSSin^.Next <= pFreeTSS;\r
+ MOV [EAX+NextTSS],EBX ;\r
+ MOV DWORD PTR [EAX+TSS_pJCB], 0 ; Make TSS invalid\r
+ MOV pFreeTSS,EAX ; pFreeTSS <= pTSSin;\r
+ INC _nTSSLeft ;\r
+\r
+ POP EBX\r
+ ;\r
+ OR ESI, ESI ;Is EDI the new Tail? (ESI = 0)\r
+ JZ RemRdyLoop1 ;Yes. Next Queue please\r
+ JMP RemRdy2 ;back to check next TSS\r
+\r
+\r
+;=============================================================================\r
+; GetExchOwner (NEAR)\r
+;\r
+; This routine returns the owner of the exchange specified.\r
+; A pointer to the JCB of the owner is returned.\r
+; ErcNotAlloc is returned if the exchange isn't allocated.\r
+; ErcOutofRange is returned is the exchange number is invalid (too high)\r
+;\r
+; Procedureal Interface :\r
+;\r
+; GetExchOwner(long Exch, char *pJCBRet): dErrror\r
+;\r
+; Exch is the exchange number.\r
+; pJCBRet is a pointer to the JCB that the tasks to kill belong to.\r
+;\r
+; Exch EQU DWORD PTR [EBP+12]\r
+; pJCBRet EQU DWORD PTR [EBP+8]\r
+\r
+PUBLIC _GetExchOwner: ;\r
+ PUSH EBP ;\r
+ MOV EBP,ESP ;\r
+\r
+ MOV EAX, [EBP+12] ; Get Resp Exchange in EDX\r
+ CMP EAX,nExch ; Is the exchange out of range?\r
+ JB GEO01 ; No, continue\r
+ MOV EAX,ErcOutOfRange ; Yes, Error in EAX register\r
+ JMP GEOEnd ;\r
+GEO01:\r
+ MOV EDX,sEXCH ; Compute offset of Exch in rgExch\r
+ MUL EDX ; sExch * Exch number\r
+ MOV EDX,prgExch ; Add offset of rgExch => EAX\r
+ ADD EDX,EAX ; EDX -> Exch\r
+ MOV EAX, [EDX+Owner]\r
+ OR EAX, EAX ; Valid Exch (Allocated)\r
+ JNZ GEO02\r
+ MOV EAX, ErcNotAlloc ; No, not allocated\r
+ JMP SHORT GEOEnd\r
+GEO02:\r
+ MOV ESI, [EBP+8] ;Where to return pJCB of Exchange\r
+ MOV [ESI], EAX ;\r
+ XOR EAX, EAX\r
+GEOEnd:\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETN 8 ;\r
+\r
+;=============================================================================\r
+; SetExchOwner (NEAR)\r
+;\r
+; This routine sets the owner of the exchange specified to the\r
+; pJCB specified. This is used by the Job code to set the owner of\r
+; a TSS exchange to a new JCB (even though the exchange was allocated\r
+; by the OS). No error checking is done as the job code does it upfront!\r
+;\r
+; Procedureal Interface :\r
+;\r
+; SetExchOwner(long Exch, char *pNewJCB): dErrror\r
+;\r
+; Exch is the exchange number.\r
+; pNewJCB is a pointer to the JCB of the new owner.\r
+;\r
+; Exch EQU DWORD PTR [EBP+12]\r
+; pNewJCB EQU DWORD PTR [EBP+8]\r
+\r
+PUBLIC _SetExchOwner: ;\r
+ PUSH EBP ;\r
+ MOV EBP,ESP ;\r
+ MOV EAX, [EBP+12] ; Exchange Number\r
+ MOV EDX,sEXCH ; Compute offset of Exch in rgExch\r
+ MUL EDX ; sExch * Exch number\r
+ MOV EDX,prgExch ; Add offset of rgExch => EAX\r
+ ADD EAX,EDX ; EAX -> oExch + prgExch\r
+ MOV EBX, [EBP+8]\r
+ MOV [EAX+Owner], EBX\r
+ XOR EAX, EAX\r
+ POP EBP ;\r
+ RETN 8 ;\r
+\r
+;=============================================================================\r
+; SendAbort (NEAR)\r
+;\r
+; This routine sends one abort message to each valid service\r
+; with the jobnum of the aborting job. If we receive a\r
+; kernel error on Request it may be becuase it is a service\r
+; that is aborting itself. We ignore the kernel errors.\r
+;\r
+; Procedureal Interface :\r
+;\r
+; SendAbort(long JobNum, ValidExch): dErrror\r
+;\r
+; JobNum is the job that is aborting\r
+; ValidExch is any valid exchange so the request will go through\r
+;\r
+; JobNum EQU DWORD PTR [EBP+12]\r
+; ValidExch EQU DWORD PTR [EBP+8]\r
+\r
+PUBLIC _SendAbort: ;\r
+ PUSH EBP ;\r
+ MOV EBP,ESP ;\r
+\r
+ MOV ESI,OFFSET rgSVC ; Get the address of rgSVC\r
+ MOV ECX,nSVC ; Get the number of Service Descriptors\r
+SAB01:\r
+ CMP DWORD PTR [ESI], 0 ; Valid name?\r
+ JE SAB05 ; NO, next service\r
+\r
+ PUSH ESI ;Save count and pointer to SVC name\r
+ PUSH ECX\r
+\r
+ ;Push all the params to make the request\r
+ PUSH ESI ;pName\r
+ PUSH 0 ;Abort Service Code\r
+ MOV EAX, [EBP+8] ;Exchange\r
+ PUSH EAX\r
+ PUSH OFFSET dJunk ;pHandleRet\r
+ PUSH 0 ;npSend\r
+ PUSH 0 ;pData0\r
+ PUSH 0 ;cbData0\r
+ PUSH 0 ;pData1\r
+ PUSH 0 ;cbData1\r
+ MOV EAX, [EBP+12] ;JobNum\r
+ PUSH EAX ;dData0\r
+ PUSH 0 ;dData1\r
+ PUSH 0 ;dData2\r
+ CALL FWORD PTR _Request\r
+\r
+;RAB\r
+SAB02:\r
+ PUSH DWORD PTR [EBP+8]\r
+ PUSH OFFSET dmsg\r
+ CALL FWORD PTR _WaitMsg\r
+ MOV EAX, dmsg\r
+ CMP EAX, dJunk ;see if we got got response back!\r
+ JNE SAB02 ;NO - wait again\r
+;RAB\r
+\r
+ POP ECX\r
+ POP ESI\r
+SAB05:\r
+ ADD ESI, sSVC ;Next Service name\r
+ LOOP SAB01\r
+ XOR EAX, EAX\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETN 8 ;\r
+\r
+\r
+\r
+;=======================================================================\r
+;============== BEGIN PUBLIC KERNEL PRIMITIVES =========================\r
+;=======================================================================\r
+;\r
+; Request - The kernel request primitive sends a message like the Send\r
+; primitive except this function requires several more parameters.\r
+; A system structure called a request block is allocated and some of these\r
+; parameters are placed in it. A request block is the basic\r
+; structure used for Client-Server communications. The exchange where a\r
+; request should be queued is determined by searching the system service\r
+; array for a matching request service name specified in the request block.\r
+; The procedural interface to Request looks like this:\r
+;\r
+; Request( pSvcName [EBP+56]\r
+; wSvcCode [EBP+52]\r
+; dRespExch [EBP+48]\r
+; pRqHndlRet [EBP+44]\r
+; dnpSend [EBP+40]\r
+; pData1 [EBP+36]\r
+; dcbData1 [EBP+32]\r
+; pData2 [EBP+28]\r
+; dcbData2 [EBP+24]\r
+; dData0 [EBP+20]\r
+; dData1 [EBP+16]\r
+; dData2 [EBP+12] ) : dError\r
+\r
+\r
+PUBLIC __Request: ;\r
+ PUSH EBP ; Save the Previous FramePtr\r
+ MOV EBP,ESP ; Set up New FramePtr\r
+\r
+ ;Validate service name from registry and get exchange\r
+ MOV EAX, [EBP+56] ;pServiceName\r
+ CALL GetExchange ;Leaves Service Exch in ESI if no errors\r
+ OR EAX,EAX ;Any errors?\r
+ JZ SHORT Req02 ;No\r
+ JMP ReqEnd ;Yes, return error\r
+Req02:\r
+ ;Validate exchange\r
+ MOV EDX, [EBP+48] ; Get Resp Exchange in EDX\r
+ CMP EDX,nExch ; Is the exchange out of range?\r
+ JB Req03 ; No, continue\r
+ MOV EAX,ercOutOfRange ; Yes, Error in EAX register\r
+ JMP ReqEnd ;\r
+Req03:\r
+ ;Get them a request block\r
+ CLI\r
+ CALL NewRQB ;EAX has ptr to new RqBlk (or 0 if none)\r
+ STI\r
+ OR EAX, EAX ;Did we get one? (NIL (0) means we didn't)\r
+ JNZ Req04 ;Yes. EAX ptr to new RqBlk\r
+ MOV EAX, ErcNoMoreRqBlks ;No, Sorry...\r
+ JMP ReqEnd ;\r
+Req04:\r
+ ;ESI still has the exchange for the service\r
+ ;EDX still has the response exchange\r
+ ;EAX has pRqBlk (Handle)\r
+\r
+ MOV EBX, EAX ;EBX now pts to RqBlk\r
+ MOV [EBX+ServiceExch], ESI ;Put Svc Exch into RqBlk\r
+ MOV EAX, [EBP+52] ;Get Svc Code\r
+ MOV [EBX+ServiceCode], AX ;Put Svc Code into RqBlk\r
+ MOV [EBX+RespExch], EDX ;Put Resp Exch into RqBlk\r
+ CALL GetCrntJobNum ;Get crnt JCB (Job Num of owner)\r
+ MOV [EBX+RqOwnerJob], EAX ;put in RqBlk\r
+ MOV EAX, [EBP+20] ;Get dData0\r
+ MOV [EBX+dData0], EAX ;put in RqBlk\r
+ MOV EAX, [EBP+16] ;Get dData1\r
+ MOV [EBX+dData1], EAX ;put in RqBlk\r
+ MOV EAX, [EBP+20] ;Get dData2\r
+ MOV [EBX+dData2], EAX ;put in RqBlk\r
+ MOV EAX, [EBP+36] ;Get pData1\r
+ MOV [EBX+pData1], EAX ;put in RqBlk\r
+ MOV EAX, [EBP+32] ;Get cbData1\r
+ MOV [EBX+cbData1], EAX ;put in RqBlk\r
+ MOV EAX, [EBP+28] ;Get pData2\r
+ MOV [EBX+pData2], EAX ;put in RqBlk\r
+ MOV EAX, [EBP+24] ;Get cbData2\r
+ MOV [EBX+cbData2], EAX ;put in RqBlk\r
+ MOV EAX, [EBP+40] ;Number of Send PbCbs\r
+ CMP EAX, 3 ;Must be 2 or less\r
+ JB Req06 ;\r
+ MOV EAX, 2\r
+Req06:\r
+ MOV [EBX+npSend], AL ;Put nSend PbCbs into RqBlk\r
+ MOV CL, 2 ;Caculate nRecv (2-nSend)\r
+ SUB CL, AL ;Leave in CL\r
+ MOV [EBX+npRecv], CL ;Put npRecv in RqBlk\r
+\r
+ ;At this point the RqBlk is all filled in.\r
+ ;Now we will return the RqBlkHandle to the user.\r
+ ;The handle is actually a ptr to the RqBlk but they can't use\r
+ ;it as one anyway (so no problem)\r
+\r
+ MOV EDI, [EBP+44] ;Ptr to return handle to\r
+ MOV [EDI], EBX ;Give it to them\r
+ MOV EDX, EBX ;Save RqBlk in EDX\r
+\r
+ CLI ; No interruptions from here on\r
+ ;Now we allocate a Link block to use\r
+\r
+ MOV EAX,pFreeLB ; EAX <= pFreeLB;\r
+ OR EAX,EAX ; Is pFreeLB NIL? (out of LBs)\r
+ JNZ Req08 ;\r
+ CALL DisposeRQB ; NO... free up RqBlk\r
+ MOV EAX,ercNoMoreLBs ; Move error in the EAX register\r
+ JMP ReqEnd ; Go home with bad news\r
+Req08:\r
+ MOV EBX,[EAX+NextLB] ; pFreeLB <= pFreeLB^.Next\r
+ MOV pFreeLB,EBX ;\r
+ DEC _nLBLeft ;\r
+\r
+ MOV DWORD PTR [EAX+LBType],REQLB ; This is a Request Link Block\r
+ MOV DWORD PTR [EAX+NextLB], 0 ; pLB^.Next <= NIL;\r
+ MOV [EAX+DataLo],EDX ; RqHandle into Lower 1/2 of Msg\r
+ MOV DWORD PTR [EAX+DataHi], 0 ; Store zero in upper half of pLB^.Data\r
+ PUSH EAX ; Save pLB on the stack\r
+\r
+ ;ESI still has the exchange Number for the service.\r
+ ;The ptr to the exch is required for deQueueTSS so we get it.\r
+\r
+ MOV EAX,ESI ; Exch => EAX\r
+ MOV EDX, sEXCH ; Compute offset of Exch in rgExch\r
+ MUL EDX ;\r
+ MOV EDX,prgExch ; Add offset of rgExch => EAX\r
+ ADD EAX,EDX ;\r
+ MOV ESI,EAX ; MAKE ESI <= pExch\r
+\r
+ ;ESI now points to the exchange\r
+\r
+ CALL deQueueTSS ; DeQueue a TSS on that Exch\r
+ OR EAX,EAX ; Did we get one?\r
+ JNZ Req10 ; Yes, give up the message\r
+ POP EAX ; No, Get the pLB just saved\r
+ CALL enQueueMsg ; EnQueue the Message on Exch\r
+ XOR EAX,EAX ; No Error\r
+ JMP SHORT ReqEnd ; And get out!\r
+Req10:\r
+ POP EBX ; Get the pLB just saved into EBX\r
+ MOV [EAX+pLBRet],EBX ; and put it in the TSS\r
+ CALL enQueueRdy ; EnQueue the TSS on the RdyQ\r
+ MOV EAX,pRunTSS ; Get the Ptr To the Running TSS\r
+ CALL enQueueRdy ; and put him on the RdyQ\r
+ CALL deQueueRdy ; Get high priority TSS off the RdyQ\r
+ CMP EAX,pRunTSS ; If the high priority TSS is the\r
+ JNE Req12 ; same as the Running TSS then return\r
+ XOR EAX,EAX ; Return to Caller with erc ok.\r
+ JMP SHORT ReqEnd\r
+Req12:\r
+ MOV pRunTSS,EAX ; Make the TSS in EAX the Running TSS\r
+ MOV BX,[EAX+Tid] ; Get the task Id (TR)\r
+ MOV TSS_Sel,BX ; Put it in the JumpAddr for Task Swtich\r
+ INC _nSwitches ; Keep track of how many swtiches for stats\r
+ MOV EAX, TimerTick ;Save time of this switch for scheduler\r
+ MOV SwitchTick, EAX ;\r
+ JMP FWORD PTR [TSS] ; JMP TSS (This is the task swtich)\r
+ XOR EAX,EAX ; Return to Caller with erc ok.\r
+ReqEnd:\r
+ STI ;\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETF 48 ; Rtn to Caller & Remove Params from stack\r
+\r
+;=============================================================================\r
+; The response primitive is used by system services to respond to a\r
+; Request received at their service exchange. The RqBlk handle must be\r
+; supplied along with the error/status code to be returned to the\r
+; caller. This is very similar to Send except is dealiases addresses\r
+; in the RqBlk and then deallocates it. The exchange to respond to\r
+; is located inside the RqBlk.\r
+; If dStatRet is ErcOwnerAbort, simply return the Reqest Block\r
+; to the free pool and return Erc 0 to caller.\r
+; Respond(dRqHndl, dStatRet): dError\r
+;\r
+;\r
+dRqHndl EQU DWORD PTR [EBP+16]\r
+dStatRet EQU DWORD PTR [EBP+12]\r
+\r
+PUBLIC __Respond: ;\r
+ PUSH EBP ; Save Callers Frame\r
+ MOV EBP,ESP ; Setup Local Frame\r
+;RAB\r
+ MOV EAX, dRqHndl ; pRqBlk into EAX\r
+ MOV EBX, dStatRet\r
+ CMP EBX, ErcOwnerAbort ;\r
+ JNE Resp01\r
+ CLI ; No interruptions\r
+ CALL DisposeRQB ; Return Aborted RQB to pool.\r
+ XOR EAX, EAX ; No Error\r
+ JMP RespEnd ; Get out\r
+Resp01:\r
+;RAB\r
+ MOV ESI, [EAX+RespExch] ; Response Exchange into ESI\r
+ CMP ESI,nExch ; Is the exchange out of range?\r
+ JNAE Resp02 ; No, continue\r
+ MOV EAX,ercOutOfRange ; Error into the EAX register.\r
+ JMP RespEnd ; Get out\r
+Resp02:\r
+ MOV EAX,ESI ; Exch => EAX\r
+ MOV EDX,sEXCH ; Compute offset of Exch in rgExch\r
+ MUL EDX ;\r
+ MOV EDX,prgExch ; Add offset of rgExch => EAX\r
+ ADD EAX,EDX ;\r
+ MOV ESI,EAX ; MAKE ESI <= pExch\r
+ CMP DWORD PTR [EAX+Owner], 0 ; If the exchange is not allocated\r
+ JNE Resp04 ; return to the caller with error\r
+ MOV EAX,ercNotAlloc ; in the EAX register.\r
+ JMP RespEnd ;\r
+Resp04:\r
+ MOV EAX, dRqHndl ; Get Request handle into EBX (pRqBlk)\r
+ MOV EBX, [EAX+RqOwnerJob]\r
+ CALL GetCrntJobNum\r
+ CMP EAX, EBX\r
+ JE Resp06 ;Same job - no DeAlias needed\r
+\r
+ MOV EAX, dRqHndl ; Get Request handle into EBX (pRqBlk)\r
+ MOV EBX, [EAX+cbData1] ;\r
+ OR EBX, EBX\r
+ JZ Resp05 ;No need to dealias (zero bytes)\r
+ MOV EDX, [EAX+pData1]\r
+ OR EDX, EDX\r
+ JZ Resp05 ;Null pointer!\r
+\r
+ PUSH ESI ;Save pExch across call\r
+\r
+ PUSH EDX ;pMem\r
+ PUSH EBX ;cbMem\r
+ CALL GetCrntJobNum\r
+ PUSH EAX\r
+ CALL FWORD PTR _DeAliasMem ;DO it and ignore errors\r
+ POP ESI ;get pExch back\r
+Resp05:\r
+ MOV EAX, dRqHndl ; Get Request handle into EBX (pRqBlk)\r
+ MOV EBX, [EAX+cbData2] ;\r
+ OR EBX, EBX\r
+ JZ Resp06 ;No need to dealias (zero bytes)\r
+ MOV EDX, [EAX+pData2]\r
+ OR EDX, EDX\r
+ JZ Resp06 ;Null pointer!\r
+ PUSH ESI ;Save pExch across call\r
+\r
+ PUSH EDX ;pMem\r
+ PUSH EBX ;cbMem\r
+ CALL GetCrntJobNum ;\r
+ PUSH EAX\r
+ CALL FWORD PTR _DeAliasMem ;DO it and ignore errors\r
+ POP ESI ;get pExch back\r
+Resp06:\r
+ MOV EAX, dRqHndl ; Get Request handle into EBX (pRqBlk)\r
+ CLI ; No interruptions\r
+ CALL DisposeRQB ; Return Rqb to pool. Not needed anymore\r
+\r
+ ; Allocate a link block\r
+ MOV EAX,pFreeLB ; NewLB <= pFreeLB;\r
+ OR EAX,EAX ; IF pFreeLB=NIL THEN No LBs;\r
+ JNZ Resp07 ;\r
+ MOV EAX,ercNoMoreLBs ; caller with error in the EAX register\r
+ JMP RespEnd\r
+Resp07:\r
+ MOV EBX,[EAX+NextLB] ; pFreeLB <= pFreeLB^.Next\r
+ MOV pFreeLB,EBX ;\r
+ DEC _nLBLeft ;\r
+\r
+ MOV DWORD PTR [EAX+LBType], RESPLB ; This is a Response Link Block\r
+ MOV DWORD PTR [EAX+NextLB], 0 ; pLB^.Next <= NIL;\r
+ MOV EBX, dRqHndl ; Get Request handle into EBX\r
+ MOV [EAX+DataLo],EBX ; Store in lower half of pLB^.Data\r
+ MOV EBX, dStatRet ; Get Status/Error into EBX\r
+ MOV [EAX+DataHi],EBX ; Store in upper half of pLB^.Data\r
+ PUSH EAX ; Save pLB on the stack\r
+ CALL deQueueTSS ; DeQueue a TSS on that Exch\r
+ OR EAX,EAX ; Did we get one?\r
+ JNZ Resp08 ; Yes, give up the message\r
+ POP EAX ; Get the pLB just saved\r
+ CALL enQueueMsg ; EnQueue the Message on Exch\r
+ XOR EAX, EAX ; No Error\r
+ JMP SHORT RespEnd ; And get out!\r
+Resp08:\r
+ POP EBX ; Get the pLB just saved into EBX\r
+ MOV [EAX+pLBRet],EBX ; and put it in the TSS\r
+ CALL enQueueRdy ; EnQueue the TSS on the RdyQ\r
+ MOV EAX,pRunTSS ; Get the Ptr To the Running TSS\r
+ CALL enQueueRdy ; and put him on the RdyQ\r
+ CALL deQueueRdy ; Get high priority TSS off the RdyQ\r
+\r
+ CMP EAX,pRunTSS ; If the high priority TSS is the\r
+ JNE Resp10 ; same as the Running TSS then return\r
+ XOR EAX,EAX ; Return to Caller with erc ok.\r
+ JMP SHORT RespEnd ;\r
+Resp10:\r
+ MOV pRunTSS,EAX ; Make the TSS in EAX the Running TSS\r
+ MOV BX,[EAX+Tid] ; Get the task Id (TR)\r
+ MOV TSS_Sel,BX ; Put it in the JumpAddr\r
+ INC _nSwitches\r
+ MOV EAX, TimerTick ;Save time of this switch for scheduler\r
+ MOV SwitchTick, EAX ;\r
+ JMP FWORD PTR [TSS] ; JMP TSS\r
+ XOR EAX,EAX ; Return to Caller with erc ok.\r
+RespEnd:\r
+ STI\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETF 8 ; Rtn to Caller & Remove Params\r
+\r
+;=============================================================================\r
+;\r
+; MoveRequest - The kernel Move Request primitive.\r
+; This allows a service to move a request to another exchange it owns.\r
+; This can not be used to forward a request to another service or Job.\r
+; It is very similar to send except it checks to ensure the destination\r
+; Exchange is owned by the sender.\r
+;\r
+; Procedural Interface :\r
+;\r
+; MoveRequest(dRqBlkHndl, DestExch):ercType\r
+;\r
+; dqMsg is the handle of the RqBlk to forward.\r
+; DestExch the exchange to where the Request should be sent.\r
+;\r
+;\r
+;dRqBlkHndl EQU [EBP+16]\r
+;DestExch EQU [EBP+12]\r
+\r
+PUBLIC __MoveRequest: ;\r
+ PUSH EBP ;\r
+ MOV EBP,ESP ;\r
+ MOV ESI, [EBP+12] ; Get Exchange Parameter in ESI\r
+ CMP ESI,nExch ; Is the exchange is out of range\r
+ JNAE MReq02 ; No, continue\r
+ MOV EAX,ercOutOfRange ; in the EAX register.\r
+ JMP MReqEnd ; Get out\r
+MReq02:\r
+ MOV EAX,ESI ; Exch => EAX\r
+ MOV EDX,sEXCH ; Compute offset of Exch in rgExch\r
+ MUL EDX ;\r
+ MOV EDX,prgExch ; Add offset of rgExch => EAX\r
+ ADD EAX,EDX ;\r
+ MOV ESI,EAX ; MAKE ESI <= pExch\r
+ MOV EDX, [EAX+Owner] ; Put exch owner into EDX (pJCB)\r
+ CALL GetpCrntJCB ; Leaves it in EAX (uses only EAX)\r
+ CMP EDX, EAX ; If the exchange is not owned by sender\r
+ JE MReq04 ; return to the caller with error\r
+ MOV EAX, ErcNotOwner ; in the EAX register.\r
+ JMP MReqEnd ; Get out\r
+MReq04:\r
+ CLI ; No interruptions from here on\r
+ ; Allocate a link block\r
+ MOV EAX,pFreeLB ; NewLB <= pFreeLB;\r
+ OR EAX,EAX ; IF pFreeLB=NIL THEN No LBs;\r
+ JNZ MReq08 ;\r
+ MOV EAX,ercNoMoreLBs ; caller with error in the EAX register\r
+ JMP MReqEnd ; Go home with bad news\r
+MReq08:\r
+ MOV EBX,[EAX+NextLB] ; pFreeLB <= pFreeLB^.Next\r
+ MOV pFreeLB,EBX ;\r
+ DEC _nLBLeft ;\r
+\r
+ MOV DWORD PTR [EAX+LBType], REQLBA ; Request Link Block (ALIASED! RAB)\r
+ MOV DWORD PTR [EAX+NextLB], 0 ; pLB^.Next <= NIL;\r
+ MOV EBX, [EBP+16] ; RqHandle\r
+ MOV [EAX+DataLo],EBX ; RqHandle into Lower 1/2 of Msg\r
+ MOV DWORD PTR [EAX+DataHi], 0 ; Store zero in upper half of pLB^.Data\r
+ PUSH EAX ; Save pLB on the stack\r
+ CALL deQueueTSS ; DeQueue a TSS on that Exch\r
+ OR EAX,EAX ; Did we get one?\r
+ JNZ MReq10 ; Yes, give up the message\r
+ POP EAX ; Get the pLB just saved\r
+ CALL enQueueMsg ; EnQueue the Message on Exch ESI\r
+ XOR EAX, EAX\r
+ JMP SHORT MReqEnd ; And get out!\r
+MReq10:\r
+ POP EBX ; Get the pLB just saved into EBX\r
+ MOV [EAX+pLBRet],EBX ; and put it in the TSS\r
+ CALL enQueueRdy ; EnQueue the TSS on the RdyQ\r
+ MOV EAX,pRunTSS ; Get the Ptr To the Running TSS\r
+ CALL enQueueRdy ; and put him on the RdyQ\r
+ CALL deQueueRdy ; Get high priority TSS off the RdyQ\r
+ CMP EAX,pRunTSS ; If the high priority TSS is the\r
+ JNE MReq12 ; same as the Running TSS then return\r
+ XOR EAX,EAX ; Return to Caller with erc ok.\r
+ JMP SHORT MReqEnd\r
+MReq12:\r
+ MOV pRunTSS,EAX ; Make the TSS in EAX the Running TSS\r
+ MOV BX,[EAX+Tid] ; Get the task Id (TR)\r
+ MOV TSS_Sel,BX ; Put it in the JumpAddr for Task Swtich\r
+ INC _nSwitches ; Keep track of how many swtiches for stats\r
+ MOV EAX, TimerTick ;Save time of this switch for scheduler\r
+ MOV SwitchTick, EAX ;\r
+ JMP FWORD PTR [TSS] ; JMP TSS (This is the task switch)\r
+ XOR EAX,EAX ; Return to Caller with erc ok.\r
+MReqEnd:\r
+ STI ;\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETF 8 ;\r
+\r
+;=============================================================================\r
+;\r
+; SendMsg - The kernel send primitive. This sends a non-specific message\r
+; from a running task to an exchange. This may cause a task swtich if\r
+; a task is waiting at the exchange and it is of equal or higher priority\r
+; that the task that sent the message.\r
+;\r
+; Procedural Interface :\r
+;\r
+; SendMsg(exch, dMsg1, dMsg2):ercType\r
+;\r
+; exch is a DWORD (4 BYTES) containing the exchange to where the\r
+; message should be sent.\r
+;\r
+; dMsg1 & dMsg2 are DWord values defined and understood\r
+; only by the sending and receiving tasks.\r
+;\r
+SendExchange EQU [EBP+14h]\r
+MessageHi EQU DWORD PTR [EBP+10h]\r
+MessageLo EQU DWORD PTR [EBP+0Ch]\r
+\r
+PUBLIC __SendMsg: ;\r
+ PUSH EBP ;\r
+ MOV EBP,ESP ;\r
+ MOV ESI,SendExchange ; Get Exchange Parameter in ESI\r
+ CMP ESI,nExch ; If the exchange is out of range\r
+ JNAE Send00 ; the return to caller with error\r
+ MOV EAX,ercOutOfRange ; in the EAX register.\r
+ JMP SendEnd\r
+Send00:\r
+ MOV EAX,ESI ; Exch => EAX\r
+ MOV EDX,sEXCH ; Compute offset of Exch in rgExch\r
+ MUL EDX ;\r
+ MOV EDX,prgExch ; Add offset of rgExch => EAX\r
+ ADD EAX,EDX ;\r
+ MOV ESI,EAX ; MAKE ESI <= pExch\r
+ CMP DWORD PTR [EAX+Owner], 0 ; If the exchange is not allocated\r
+ JNE Send01 ; return to the caller with error\r
+ MOV EAX,ercNotAlloc ; in the EAX register.\r
+ JMP SendEnd ;\r
+Send01:\r
+ CLI ; No interrupts\r
+ ; Allocate a link block\r
+ MOV EAX,pFreeLB ; NewLB <= pFreeLB;\r
+ OR EAX,EAX ; IF pFreeLB=NIL THEN No LBs;\r
+ JNZ SHORT Send02 ;\r
+ MOV EAX,ercNoMoreLBs ; caller with error in the EAX register\r
+ JMP SHORT MReqEnd ; Go home with bad news\r
+Send02:\r
+ MOV EBX,[EAX+NextLB] ; pFreeLB <= pFreeLB^.Next\r
+ MOV pFreeLB,EBX ;\r
+ DEC _nLBLeft ;\r
+\r
+ MOV DWORD PTR [EAX+LBType], DATALB ; This is a Data Link Block\r
+ MOV DWORD PTR [EAX+NextLB], 0 ; pLB^.Next <= NIL;\r
+ MOV EBX,MessageLo ; Get lower half of Msg in EBX\r
+ MOV [EAX+DataLo],EBX ; Store in lower half of pLB^.Data\r
+ MOV EBX,MessageHi ; Get upper half of Msg in EBX\r
+ MOV [EAX+DataHi],EBX ; Store in upper half of pLB^.Data\r
+\r
+ PUSH EAX ; Save pLB on the stack\r
+\r
+ CLI ; No interrupts\r
+ CALL deQueueTSS ; DeQueue a TSS on that Exch\r
+ STI\r
+ OR EAX,EAX ; Did we get one?\r
+ JNZ Send25 ; Yes, give up the message\r
+ POP EAX ; Get the pLB just saved\r
+ CLI ; No interrupts\r
+ CALL enQueueMsg ; EnQueue the Message on Exch\r
+ JMP Send04 ; And get out (Erc 0)!\r
+Send25:\r
+ POP EBX ; Get the pLB just saved into EBX\r
+ CLI ; No interrupts\r
+ MOV [EAX+pLBRet],EBX ; and put it in the TSS\r
+ CALL enQueueRdy ; EnQueue the TSS on the RdyQ\r
+ MOV EAX,pRunTSS ; Get the Ptr To the Running TSS\r
+ CALL enQueueRdy ; and put him on the RdyQ\r
+ CALL deQueueRdy ; Get high priority TSS off the RdyQ\r
+ CMP EAX,pRunTSS ; If the high priority TSS is the\r
+ JNE Send03 ; same as the Running TSS then return\r
+ JMP SHORT Send04 ; Return with ErcOk\r
+\r
+Send03:\r
+ MOV pRunTSS,EAX ; Make the TSS in EAX the Running TSS\r
+ MOV BX,[EAX+Tid] ; Get the task Id (TR)\r
+ MOV TSS_Sel,BX ; Put it in the JumpAddr\r
+ INC _nSwitches\r
+ MOV EAX, TimerTick ;Save time of this switch for scheduler\r
+ MOV SwitchTick, EAX ;\r
+ JMP FWORD PTR [TSS] ; JMP TSS\r
+Send04:\r
+ XOR EAX,EAX ; Return to Caller with erc ok.\r
+SendEnd:\r
+ STI ;\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETF 12 ;\r
+\r
+;=============================================================================\r
+;\r
+; ISendMsg - The OS Interrupt Send primitive.\r
+; This procedure allows an ISR to send a message to an exchange.\r
+; This is the same as SendMsg except NO task switch is\r
+; performed. If a task is waiting at the exchange, the message is\r
+; associated (linked) with it and it is moved to the RdyQ.\r
+; It will get a chance to run the next time the RdyQ is evaluated\r
+; by the Kernel which will probably be by the timer interrupt slicer.\r
+; Interrupt tasks can use ISendMsg to send single or multiple messages\r
+; to exchanges during their execution.\r
+; Interrupts are CLEARED on entry and WILL NOT BE SET on exit!!!\r
+; It is the responsibility of the caller to set them if desired.\r
+; ISendMsg is intended only to be used by ISRs in device drivers\r
+;\r
+;\r
+; Procedural Interface :\r
+;\r
+; ISendMsg(exch, dMsg1, dMsg2):ercType\r
+;\r
+; exch is a DWORD (4 BYTES) containing the exchange to where the\r
+; message should be sent.\r
+;\r
+; dMsg1 and dMsg2 are DWORD messages.\r
+;\r
+; Parameters on stack are the same as _SendMsg.\r
+\r
+PUBLIC __ISendMsg: ;\r
+ CLI ;INTS ALWAYS CLEARED AND LEFT THAT WAY!\r
+ PUSH EBP ;\r
+ MOV EBP,ESP ;\r
+ MOV ESI,SendExchange ; Get Exchange Parameter in ESI\r
+ CMP ESI,nExch ; If the exchange is out of range\r
+ JNAE ISend00 ; then return to caller with error\r
+ MOV EAX,ercOutOfRange ; in the EAX register.\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETF 12 ;\r
+ISend00:\r
+ MOV EAX,ESI ; Exch => EAX\r
+ MOV EDX,sEXCH ; Compute offset of Exch in rgExch\r
+ MUL EDX ;\r
+ MOV EDX,prgExch ; Add offset of rgExch => EAX\r
+ ADD EAX,EDX ;\r
+ MOV ESI,EAX ; MAKE ESI <= pExch\r
+ CMP DWORD PTR [EAX+Owner], 0 ; If the exchange is not allocated\r
+ JNE ISend01 ; return to the caller with error\r
+ MOV EAX,ercNotAlloc ; in the EAX register.\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETF 12 ;\r
+ISend01:\r
+ ; Allocate a link block\r
+ MOV EAX,pFreeLB ; NewLB <= pFreeLB;\r
+ OR EAX,EAX ; IF pFreeLB=NIL THEN No LBs;\r
+ JNZ SHORT ISend02 ;\r
+ MOV EAX,ercNoMoreLBs ; caller with error in the EAX register\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETF 12 ;\r
+ISend02:\r
+ MOV EBX,[EAX+NextLB] ; pFreeLB <= pFreeLB^.Next\r
+ MOV pFreeLB,EBX ;\r
+ DEC _nLBLeft ;\r
+\r
+ MOV DWORD PTR [EAX+LBType], DATALB ; This is a Data Link Block\r
+ MOV DWORD PTR [EAX+NextLB],0 ; pLB^.Next <= NIL;\r
+ MOV EBX,MessageLo ; Get lower half of Msg in EBX\r
+ MOV [EAX+DataLo],EBX ; Store in lower half of pLB^.Data\r
+ MOV EBX,MessageHi ; Get upper half of Msg in EBX\r
+ MOV [EAX+DataHi],EBX ; Store in upper half of pLB^.Data\r
+ PUSH EAX ; Save pLB on the stack\r
+ CALL deQueueTSS ; DeQueue a TSS on that Exch\r
+ OR EAX,EAX ; Did we get one?\r
+ JNZ ISend03 ; Yes, give up the message\r
+ POP EAX ; No, Get the pLB just saved\r
+ CALL enQueueMsg ; EnQueue the Message on Exch\r
+ JMP ISend04 ; And get out!\r
+ISend03:\r
+ POP EBX ; Get the pLB just saved into EBX\r
+ MOV [EAX+pLBRet],EBX ; and put it in the TSS\r
+ CALL enQueueRdy ; EnQueue the TSS on the RdyQ\r
+ISend04:\r
+ XOR EAX,EAX ; Return to Caller with erc ok.\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETF 12 ;\r
+\r
+;=============================================================================\r
+;\r
+; Wait - The kernel wait primitive. This procedure allows a task to\r
+; receive information from another task from an exchange. If no\r
+; message is at the exchange, the task is placed on the exchange\r
+; and the ReadyQ is reevaluated to make the next tast run.\r
+;\r
+; A result code is returned in the EAX register.\r
+;\r
+; Procedural Interface :\r
+;\r
+; Wait(exch,pdqMsgRet):ercType\r
+;\r
+; exch is a DWORD (4 BYTES) containing the exchange to where the\r
+; message should be sent.\r
+;\r
+; pMessage is a pointer to an 8 byte area where the\r
+; message is stored.\r
+;\r
+WaitExchange EQU [EBP+10h]\r
+pMessage EQU [EBP+0Ch]\r
+;\r
+;\r
+PUBLIC __WaitMsg: ;\r
+ PUSH EBP ;\r
+ MOV EBP,ESP ;\r
+ MOV ESI,WaitExchange ; Get Exchange Parameter in ESI\r
+ CMP ESI,nExch ; If the exchange is out of range\r
+ JNAE Wait00 ; the return to caller with error\r
+ MOV EAX,ercOutOfRange ; in the EAX register.\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETF 8 ;\r
+Wait00:\r
+ MOV EAX,ESI ; ExchId => EAX\r
+ MOV EBX,sEXCH ; Compute offset of ExchId in rgExch\r
+ MUL EBX ;\r
+ MOV EDX,prgExch ; Add offset of rgExch => EAX\r
+ ADD EAX,EDX ;\r
+ MOV ESI,EAX ; Put Exch in to ESI\r
+ CMP DWORD PTR [EAX+Owner], 0 ; If the exchange is not allocated\r
+ JNE Wait01 ; return to the caller with error\r
+ MOV EAX,ercNotAlloc ; in the EAX register.\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETF 8 ;\r
+Wait01:\r
+ CLI ;\r
+ CALL deQueueMsg ; EAX <= pLB from pExch (ESI)\r
+ OR EAX,EAX ; If no message (pLB = NIL) Then\r
+ JZ Wait02 ; Wait for Message Else\r
+ JMP Wait05 ; Get Message and Return\r
+\r
+Wait02:\r
+ MOV EAX,pRunTSS ; Get pRunTSS in EAX to Wait\r
+\r
+ ;This next section of code Queues up the TSS pointed to\r
+ ;by EAX on the exchange pointed to by ESI\r
+ ; (i.e., we make the current task "wait")\r
+\r
+ MOV DWORD PTR [EAX+NextTSS], 0 ; pTSSin^.Next <= NIL;\r
+ XCHG ESI,EAX ; pExch => EAX, pTSSin => ESI\r
+ CMP DWORD PTR [EAX+EHead], 0 ; if ..TSSHead = NIL\r
+ JNE Wait025 ; then\r
+ MOV [EAX+EHead],ESI ; ..TSSHead <= pTSSin;\r
+ MOV [EAX+ETail],ESI ; ..TSSTail <= pTSSin;\r
+ MOV DWORD PTR [EAX+fEMsg], 0 ; Flag it as a TSS (vice a Msg)\r
+ XCHG ESI,EAX ; Make ESI <= pExch Again\r
+ JMP SHORT Wait03 ; else\r
+Wait025:\r
+ MOV EDX,[EAX+ETail] ; ..TSSTail^.NextTSS <= pTSSin;\r
+ MOV [EDX+NextTSS],ESI ;\r
+ MOV [EAX+ETail],ESI ; ..TSSTail <= pTSSin;\r
+ MOV DWORD PTR [EAX+fEMsg], 0 ; Flag it as a TSS (vice a Msg)\r
+ XCHG ESI,EAX ; Make ESI <= pExch Again\r
+\r
+ ;We just placed the current TSS on an exchange,\r
+ ;now we get the next TSS to run (if there is one)\r
+\r
+Wait03:\r
+ CALL deQueueRdy ; Get highest priority TSS off the RdyQ\r
+ OR EAX, EAX ; Anyone ready to run?\r
+ JNZ Wait035 ; Yes (jump to check pTSS)\r
+\r
+ MOV EDI, 1\r
+ MOV dfHalted, EDI\r
+ INC _nHalts\r
+ STI ; No, then HLT CPU until ready\r
+ HLT ; Halt CPU and wait for interrupt\r
+ CLI ; An interrupt has occured. Clear Interrupts\r
+ XOR EDI,EDI\r
+ MOV dfHalted, EDI\r
+ JMP Wait03 ; Check for a task to switch to\r
+\r
+Wait035:\r
+ CMP EAX,pRunTSS ; Same one as before???\r
+ JE Wait04 ; You bet! NO SWITCH!!!\r
+\r
+ ;Now we switch tasks by placing the address of the\r
+ ;new TSS in pRunTSS and jumping to it. This forces\r
+ ;a 386 task switch.\r
+\r
+ MOV pRunTSS,EAX ; Make high priority TSS Run.\r
+ MOV BX,[EAX+Tid] ;\r
+ MOV TSS_Sel,BX ;\r
+ INC _nSwitches\r
+ MOV EAX, TimerTick ;Save time of this switch for scheduler\r
+ MOV SwitchTick, EAX ;\r
+ JMP FWORD PTR [TSS] ; JUMP TSS (Switch Tasks)\r
+\r
+ ; A task has just finished "Waiting"\r
+ ; We are now in the new task with its memory space\r
+ ; (or the same task if he was high pri & had a msg)\r
+ ; If this is a system service it may need RqBlk address aliases\r
+ ; If it is an OS service we alias in OS memory!\r
+\r
+Wait04:\r
+ MOV EDX,pRunTSS ; Put the TSS in EAX into EDX\r
+ MOV EAX,[EDX+pLBRet] ; Get the pLB in EAX\r
+Wait05:\r
+ ; if we got here, we have either switched tasks\r
+ ; and we are delivering a message (or Req) to the new task,\r
+ ; or the there was a message waiting at the exch of\r
+ ; the first caller and we are delivering it.\r
+ ; Either way, the message is already deQueued from\r
+ ; the exch and the critical part of WaitMsg is over.\r
+ ; We can start interrupts again except when we have\r
+ ; to return the Link Block to the pool (free it up)\r
+\r
+ STI ; WE CAN RESTART INTERRUPTS HERE\r
+ CMP DWORD PTR [EAX+LBType],REQLB ; Is the link block a Req Link Block?\r
+ JNE Wait06 ; No, Treat it as a data link block\r
+\r
+ ;pLB.DataLo is RqHandle (pRqBlk)\r
+\r
+ PUSH EAX ; Save ptr to Link Block\r
+ MOV EBX,[EAX+DataLo] ; Get pRqBlk into EBX\r
+\r
+ ;Now we set up to alias the memory for the service\r
+ ; (Alias the 2 Pointers in the RqBlk)\r
+ ;_AliasMem(pMem, dcbMem, dJobNum, ppAliasRet): dError\r
+\r
+ MOV ECX, [EBX+cbData1] ;\r
+ OR ECX, ECX ;is cbData1 0?\r
+ JZ Wait051 ;Yes\r
+\r
+ MOV EAX, [EBX+pData1] ;\r
+ OR EAX, EAX ;is pData1 NULL?\r
+ JZ Wait051 ;Yes\r
+\r
+ ;Set up params for AliasMem\r
+ PUSH EAX ;pMem\r
+ PUSH ECX ;cbMem\r
+ MOV EAX, [EBX+RqOwnerJob]\r
+ PUSH EAX ;dJobNum\r
+ ADD EBX, pData1 ;Offset to pData1 in RqBlk\r
+ PUSH EBX ;Linear Address of pData1\r
+ CALL FWORD PTR _AliasMem\r
+ OR EAX, EAX ;Error??\r
+ JZ Wait051 ;No, continue\r
+ POP EBX ;Make stack right\r
+ MOV ESP,EBP ;Return Error...\r
+ POP EBP ;\r
+ RETF 8 ;\r
+\r
+Wait051: ;Second Pointer (pData2)\r
+ POP EAX ;Restore ptr to Link Block\r
+ PUSH EAX ;Save again\r
+ MOV EBX,[EAX+DataLo] ; Get pRqBlk into EBX\r
+\r
+ MOV ECX, [EBX+cbData2] ;\r
+ OR ECX, ECX ;is cbData2 0?\r
+ JZ Wait052 ;Yes\r
+\r
+ MOV EAX, [EBX+pData2] ;\r
+ OR EAX, EAX ;is pData2 NULL?\r
+ JZ Wait052 ;Yes\r
+ ;Set up params for AliasMem\r
+ PUSH EAX ;pMem\r
+ PUSH ECX ;cbMem\r
+ MOV EAX, [EBX+RqOwnerJob]\r
+ PUSH EAX ;dJobNum\r
+ ADD EBX, pData2 ;Offset to pData2 in RqBlk\r
+ PUSH EBX ;Linear Address of PData1\r
+ CALL FWORD PTR _AliasMem\r
+ OR EAX, EAX ;Error??\r
+ JZ Wait052 ;No, continue\r
+ POP EBX ;Make stack right\r
+ MOV ESP,EBP ;Return Error...\r
+ POP EBP ;\r
+ RETF 8 ;\r
+\r
+Wait052:\r
+ POP EAX ;Restore ptr to Link Block\r
+Wait06:\r
+ MOV EBX,[EAX+DataLo] ; Get pLB^.Data into ECX:EBX\r
+ MOV ECX,[EAX+DataHi] ;\r
+ MOV EDX,pMessage ; Get Storage Addr in EDX\r
+ MOV [EDX],EBX ; Put pLB^.Data in specified\r
+ MOV [EDX+4],ECX ; memory space (EDX)\r
+\r
+ ;Return the LB to the pool\r
+ CLI\r
+ MOV EBX,pFreeLB ; pLBin^.Next <= pFreeLB;\r
+ MOV [EAX+NextLB],EBX ;\r
+ MOV pFreeLB,EAX ; pFreeLB <= pLBin;\r
+ INC _nLBLeft ;\r
+ STI ;\r
+ XOR EAX,EAX ; ErcOK! (0)\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETF 8 ;\r
+\r
+;\r
+;=============================================================================\r
+;\r
+; CheckMsg - The kernel Check primitive. This procedure provides access\r
+; to the operating system by allowing a task to receive information\r
+; from another process WITHOUT BLOCKING. In other words, if no message is\r
+; available Check returns to the caller. If a message IS available it\r
+; is returned to the caller immediately. The caller is never placed on\r
+; an exchange and the RdyQ is not evaluated.\r
+;\r
+; A result code is returned in the EAX register.\r
+;\r
+; Procedureal Interface :\r
+;\r
+; CheckMsg(exch,pdqMsg):ercType\r
+;\r
+; exch is a DWORD (4 BYTES) containing the exchange to where the\r
+; message should be sent.\r
+;\r
+; pdqMsg is a pointer to an 8 byte area where the message is stored.\r
+;\r
+ChkExchange EQU [EBP+10h]\r
+pCkMessage EQU [EBP+0Ch]\r
+\r
+PUBLIC __CheckMsg: ;\r
+ PUSH EBP ;\r
+ MOV EBP,ESP ;\r
+ MOV ESI,ChkExchange ; Get Exchange Parameter in ESI\r
+ CMP ESI,nExch ; If the exchange is out of range\r
+ JNAE Chk01 ; the return to caller with error\r
+ MOV EAX,ercOutOfRange ; in the EAX register.\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETF 8 ;\r
+Chk01:\r
+ MOV EAX,ESI ; Exch => EAX\r
+ MOV EBX,sEXCH ; Compute offset of Exch in rgExch\r
+ MUL EBX ;\r
+ MOV EDX,prgExch ; Add offset of rgExch => EAX\r
+ ADD EAX,EDX ;\r
+ MOV ESI,EAX ; Put pExch in to ESI\r
+ CMP DWORD PTR [EAX+Owner], 0 ; If the exchange is not allocated\r
+ JNE Chk02 ; return to the caller with error\r
+\r
+ MOV EAX,ercNotAlloc ; in the EAX register.\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETF 8 ;\r
+Chk02:\r
+ CLI ; Can't be interrupted\r
+ CALL deQueueMsg ; EAX <= pLB from pExch (ESI)\r
+ OR EAX,EAX ; If pLB = NIL Then\r
+ JNZ Chk03 ; Go to get msg and return\r
+\r
+ STI ;\r
+ MOV EAX,ercNoMsg ; return with erc no msg\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETF 8 ;\r
+Chk03:\r
+ STI ;We can be interrupted again\r
+\r
+ CMP DWORD PTR [EAX+LBType],REQLB ; Is the link block a Req Link Block?\r
+ JNE Chk04 ; No, Treat it as a data link block\r
+\r
+ ;pLB.DataLo is RqHandle (pRqBlk)\r
+\r
+ PUSH EAX ; Save ptr to Link Block\r
+ MOV EBX,[EAX+DataLo] ; Get pRqBlk into EBX\r
+\r
+ ;Now we set up to alias the memory for the service\r
+ ; (Alias the 2 Pointers in the RqBlk)\r
+ ;_AliasMem(pMem, dcbMem, dJobNum, ppAliasRet): dError\r
+\r
+ MOV ECX, [EBX+cbData1] ;\r
+ OR ECX, ECX ;is cbData1 0?\r
+ JZ Chk031 ;Yes\r
+\r
+ MOV EAX, [EBX+pData1] ;\r
+ OR EAX, EAX ;is pData1 NULL?\r
+ JZ Chk031 ;Yes\r
+ ;Set up params for AliasMem\r
+ PUSH EAX ;pMem\r
+ PUSH ECX ;cbMem\r
+ MOV EAX, [EBX+RqOwnerJob]\r
+ PUSH EAX ;dJobNum\r
+ ADD EBX, pData1 ;Offset to pData1 in RqBlk\r
+ PUSH EBX ;Linear Address of pData1\r
+ CALL FWORD PTR _AliasMem\r
+ OR EAX, EAX ;Error??\r
+ JZ Chk031 ;No, continue\r
+ POP EBX ;Make stack right\r
+ MOV ESP,EBP ;Return Error...\r
+ POP EBP ;\r
+ RETF 8 ;\r
+\r
+Chk031: ;Second Pointer (pData2)\r
+ POP EAX ;Restore ptr to Link Block\r
+ PUSH EAX ;Save again\r
+ MOV EBX,[EAX+DataLo] ; Get pRqBlk into EBX\r
+\r
+ MOV ECX, [EBX+cbData2] ;\r
+ OR ECX, ECX ;is cbData2 0?\r
+ JZ Chk032 ;Yes\r
+\r
+ MOV EAX, [EBX+pData2] ;\r
+ OR EAX, EAX ;is pData2 NULL?\r
+ JZ Chk032 ;Yes\r
+ ;Set up params for AliasMem\r
+ PUSH EAX ;pMem\r
+ PUSH ECX ;cbMem\r
+ MOV EAX, [EBX+RqOwnerJob]\r
+ PUSH EAX ;dJobNum\r
+ ADD EBX, pData2 ;Offset to pData2 in RqBlk\r
+ PUSH EBX ;Linear Address of PData1\r
+ CALL FWORD PTR _AliasMem\r
+ OR EAX, EAX ;Error??\r
+ JZ Chk032 ;No, continue\r
+ POP EBX ;Make stack right\r
+ MOV ESP,EBP ;Return Error...\r
+ POP EBP ;\r
+ RETF 8 ;\r
+\r
+Chk032:\r
+ POP EAX ;Restore Ptr to Link Block\r
+\r
+Chk04:\r
+ MOV EBX,[EAX+DataLo] ; Get pLB^.Data into ECX:EBX\r
+ MOV ECX,[EAX+DataHi] ;\r
+ MOV EDX,pCkMessage ; Get Storage Addr in EDX\r
+ MOV [EDX],EBX ; Put pLB^.Data in specified\r
+ MOV [EDX+4],ECX ; memory space (EDX)\r
+\r
+ ;Return the LB to the pool\r
+ CLI\r
+ MOV EBX,pFreeLB ; pLBin^.Next <= pFreeLB;\r
+ MOV [EAX+NextLB],EBX ;\r
+ MOV pFreeLB,EAX ; pFreeLB <= pLBin;\r
+ INC _nLBLeft ;\r
+ STI ;\r
+\r
+ XOR EAX,EAX ;ErcOK! (0)\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETF 8 ;\r
+\r
+;====================================================================\r
+; NewTask --- OS PUBLIC - Creates a new task and schedules it for execution.\r
+; used primarily to create a task for a job other than the one you are in.\r
+; The OS uses this to create the initial task for a newly loaded job.\r
+;\r
+; The OS stacks are preallocated as part of the TSS. If this is an OS\r
+; task (CodeSeg = 8) then we load the TSS Stack into TSS_ESP and\r
+; TSS_ESP0, otherwise we take the ESP param and place it into TSS_ESP.\r
+;\r
+; Procedural interface:\r
+;\r
+; NewTask(JobNum, CodeSeg, Priority, fDebug, Exch, ESP, EIP): dErcRet\r
+;\r
+;\r
+NTS_Job EQU [EBP+36] ;Job Num for this task\r
+NTS_CS EQU [EBP+32] ;8 for OS, 18h for user task\r
+NTS_Pri EQU [EBP+28] ;Priority of this task\r
+NTS_fDbg EQU [EBP+24] ;TRUE for DEBUGing\r
+NTS_Exch EQU [EBP+20] ;Exchange for TSS\r
+NTS_ESP EQU [EBP+16] ;Initial stack pointer\r
+NTS_EIP EQU [EBP+12] ;Task start address\r
+\r
+PUBLIC __NewTask: ;\r
+ PUSH EBP ;\r
+ MOV EBP,ESP ;\r
+\r
+ MOV EDX, NTS_Pri ;\r
+ CMP EDX, nPRI-1 ;Priority OK?\r
+ JBE NT0000\r
+ MOV EAX,ercBadPriority\r
+ JMP NTEnd\r
+NT0000:\r
+ MOV ECX, NTS_Exch\r
+ CMP ECX, nExch ;Exch in range?\r
+ JBE NT0001\r
+ MOV EAX,ercOutOfRange\r
+ JMP NTEnd\r
+NT0001:\r
+ CLI ;we can't be interrupted\r
+ MOV EAX,pFreeTSS ; NewTSS <= pFreeTSS;\r
+ OR EAX,EAX ; IF pFreeTSS=NIL THEN Return;\r
+ JNZ NT0002 ;\r
+ MOV EAX,ercNoMoreTSSs ;No...\r
+ JMP NTEnd\r
+NT0002:\r
+ MOV EBX,[EAX+NextTSS] ; pFreeTSS <= pFreeTSS^.Next\r
+ MOV pFreeTSS,EBX ;\r
+ DEC _nTSSLeft ;\r
+ STI\r
+\r
+ ;EAX now has pNewTSS\r
+\r
+ MOV [EAX+Priority],DL ;put Priority into TSS\r
+ MOV DWORD PTR [EAX+TSS_EFlags],0202h ;Load the Flags Register\r
+ MOV [EAX+TSS_Exch], ECX ;Put new Exch in TSS (ECX is free)\r
+ MOV EBX, NTS_EIP ;mov EIP into TSS (Start Address)\r
+ MOV [EAX+TSS_EIP],EBX\r
+ MOV EBX, NTS_ESP ;mov ESP into TSS\r
+ MOV [EAX+TSS_ESP],EBX\r
+ MOV [EAX+TSS_ESP0],EBX ;\r
+ MOV ECX, NTS_CS ;mov CS into TSS\r
+ MOV [EAX+TSS_CS],CX\r
+\r
+ PUSH EAX ;Save pNewTSS\r
+\r
+ ;Now we get pJCB from JobNum they passed in so we can\r
+ ;get the PD from the JCB\r
+\r
+ MOV EAX, NTS_Job ;Set up to call GetpJCB\r
+ CALL GetpJCB ;EAX now has pJCB\r
+ MOV ECX, EAX ;ECX now has pJCB\r
+\r
+ POP EAX ;Restore pNewTSS to EAX\r
+\r
+ MOV [EAX+TSS_pJCB],ECX ;Put pJCB into TSS\r
+ MOV EBX, [ECX+JcbPD] ;Set up to call LinToPhy\r
+\r
+ PUSH EAX ;Save pNewTSS again\r
+\r
+ MOV EAX, NTS_Job ;\r
+ CALL LinToPhy ;Get Physical Address for PD into EAX\r
+ MOV EBX, EAX\r
+ POP EAX ;pNewTSS into EAX\r
+ MOV [EAX+TSS_CR3],EBX ;Put Physical Add for PD into TSS_CR3\r
+ CMP DWORD PTR NTS_fDbg, 0 ;Debug on entry?\r
+ JE NT0004 ;No\r
+ MOV WORD PTR [EAX+TSS_TrapBit], 1 ;Yes\r
+NT0004:\r
+\r
+ MOV EBX, NTS_Pri ;Get priority of new task\r
+\r
+ CLI ;We can't be interrupted\r
+ MOV EDX,pRunTSS ;Get who's running\r
+ CMP BYTE PTR [EDX+Priority],BL ;Who got the highest Pri?\r
+ JA NT0005 ;New guy does (lowest num)\r
+ CALL enQueueRdy ;Just put new guy on the ReadyQue (EAX)\r
+ XOR EAX,EAX ;ercOk\r
+ JMP NTEnd ;Return to caller\r
+NT0005:\r
+ XCHG EAX,EDX ;CrntTSS -> EAX, New TSS -> EDX\r
+ PUSH EDX ;Save New TSS\r
+ CALL enQueueRdy ;\r
+ POP EAX ;New TSS -> EAX\r
+ MOV pRunTSS,EAX ;Move new TSS into pRunTSS\r
+ MOV BX,[EAX+Tid] ;Put Selector/Offset in "TSS"\r
+ MOV TSS_Sel,BX ;\r
+ INC _nSwitches\r
+ MOV EAX, TimerTick ;Save time of this switch for scheduler\r
+ MOV SwitchTick, EAX ;\r
+ JMP FWORD PTR [TSS] ;Jump to new TSS\r
+ XOR EAX,EAX ;ErcOk\r
+NTEnd:\r
+ STI ;\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETF 28 ;\r
+\r
+;====================================================================\r
+; SpawnTask --- OS PUBLIC - Creates a new task in the current job\r
+; and schedules it for execution\r
+;\r
+; Procedural Interface:\r
+; SpawnTask(pEntry, dPriority, fDebug, pStack, fOSCode);\r
+;\r
+;\r
+pEntryST EQU DWORD PTR [EBP+28]\r
+dPriST EQU DWORD PTR [EBP+24]\r
+fDebugST EQU DWORD PTR [EBP+20]\r
+pStackST EQU DWORD PTR [EBP+16]\r
+fOSCodeST EQU DWORD PTR [EBP+12]\r
+\r
+NewExchST EQU DWORD PTR [EBP-4]\r
+NewTSSST EQU DWORD PTR [EBP-8]\r
+\r
+PUBLIC __SpawnTask: ;\r
+ PUSH EBP ;\r
+ MOV EBP,ESP ;\r
+ SUB ESP, 8 ;two local DWORD vars\r
+ CMP dPriST, nPRI-1 ;Priority OK?\r
+ JBE ST0001\r
+ MOV EAX,ercBadPriority\r
+ JMP STEnd\r
+ST0001:\r
+ LEA EAX, NewExchST ;Allocate exchange\r
+ PUSH EAX\r
+ CALL FWORD PTR _AllocExch\r
+ OR EAX, EAX ;see if we got an error\r
+ JNZ STEnd ;Yup, bad news\r
+\r
+ ;Allocate a new TSS\r
+ CLI ;we can't be interrupted\r
+ MOV EAX,pFreeTSS ; NewTSS <= pFreeTSS;\r
+ OR EAX,EAX ; IF pFreeTSS=NIL THEN Return;\r
+ JNZ ST0002 ;\r
+ STI\r
+\r
+ ;Dealloc Exch if we didn't get a TSS\r
+ PUSH NewExchST\r
+ CALL FWORD PTR _DeAllocExch\r
+ MOV EAX,ercNoMoreTSSs ;No...\r
+ JMP NTEnd\r
+ST0002:\r
+ MOV EBX,[EAX+NextTSS] ; pFreeTSS <= pFreeTSS^.Next\r
+ MOV pFreeTSS,EBX ;\r
+ DEC _nTSSLeft ;\r
+ STI\r
+\r
+ MOV NewTSSST, EAX ;Save new TSS\r
+ MOV EBX, NewExchST ;mov exch into TSS\r
+ MOV [EAX+TSS_Exch],EBX\r
+ MOV WORD PTR [EAX+TSS_CS], OSCodeSel ;Defaults to OS code selector\r
+ CMP fOSCodeST, 0\r
+ JNE ST0003\r
+ MOV WORD PTR [EAX+TSS_CS], JobCodeSel ;Make OS code selector\r
+ST0003:\r
+ MOV EBX,pEntryST ;mov EIP into TSS\r
+ MOV [EAX+TSS_EIP],EBX\r
+ MOV EBX, pStackST ;mov ESP into TSS\r
+ MOV [EAX+TSS_ESP],EBX\r
+ MOV [EAX+TSS_ESP0],EBX\r
+ MOV EBX, pRunTSS\r
+ MOV EDX, [EBX+TSS_pJCB] ;Get pJCB from Crnt Task\r
+ MOV [EAX+TSS_pJCB],EDX\r
+ MOV EDX, [EBX+TSS_CR3] ;Get CR3 from crnt task\r
+ MOV [EAX+TSS_CR3],EDX ; move into new TSS\r
+ MOV DWORD PTR [EAX+TSS_EFlags],0202h ;Load the Flags Register\r
+ CMP fDebugST, 0 ;Debug on entry?\r
+ JE ST0004 ;No\r
+ MOV WORD PTR [EAX+TSS_TrapBit], 1 ;Yes\r
+ST0004:\r
+ MOV EBX, dPriST ;mov priority into BL\r
+ MOV [EAX+Priority],BL ;put in TSS\r
+\r
+ CLI ;we can't be interrupted\r
+ MOV EDX,pRunTSS ;Get who's running\r
+ CMP [EDX+Priority],BL ;Who got the highest Pri?\r
+ JA ST0005 ;If crnt >, New guy does (lowest num)\r
+ CALL enQueueRdy ;Old guy does, just put new guy on Q.\r
+ XOR EAX,EAX ;ercOk\r
+ JMP STEnd ;Return to caller\r
+ST0005:\r
+ XCHG EAX,EDX ;CrntTSS -> EAX, New TSS -> EDX\r
+ PUSH EDX ;New TSS -> Stack\r
+ CALL enQueueRdy ;Place crnt TSS on Q\r
+ POP EAX ;New TSS -> EAX\r
+ MOV pRunTSS,EAX ;Move new TSS into pRunTSS\r
+ MOV BX,[EAX+Tid] ;Put Selector/Offset in "TSS"\r
+ MOV TSS_Sel,BX ;\r
+ INC _nSwitches\r
+ MOV EAX, TimerTick ;Save time of this switch for scheduler\r
+ MOV SwitchTick, EAX ;\r
+ JMP FWORD PTR [TSS] ;Jump to new TSS\r
+ XOR EAX,EAX ;ErcOk\r
+STEnd:\r
+ STI ;\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETF 20 ;\r
+\r
+;=============================================================================\r
+;\r
+; AllocExch - The kernel Allocate Exchange primitive. This procedure\r
+; provides access to the operating system by allowing a TASK to\r
+; allocate a message port for the transmission and reception of messages from\r
+; another process.\r
+;\r
+; Procedural Interface :\r
+;\r
+; AllocExch(pExchRet):dError\r
+;\r
+; pExchRet is a pointer to where you want the Exchange Handle\r
+; returned. The Exchange Handle is a DWORD (4 BYTES).\r
+;\r
+;=============================================================================\r
+\r
+PUBLIC __AllocExch: ;\r
+ PUSH EBP ;\r
+ MOV EBP,ESP ;\r
+\r
+ XOR ESI,ESI ; Zero the Exch Index\r
+ MOV EBX,prgExch ; EBX <= ADR rgExch\r
+ MOV ECX,nExch ; Get number of exchanges in ECX\r
+AE000:\r
+ CLI ;\r
+ CMP DWORD PTR [EBX+Owner], 0 ; Is this exchange free to use\r
+ JE AE001 ; If we found a Free Exch, JUMP\r
+ ADD EBX,sEXCH ; Point to the next Exchange\r
+ INC ESI ; Increment the Exchange Index\r
+ LOOP AE000 ; Keep looping until we are done\r
+ STI ;\r
+ MOV EAX,ercNoMoreExch ; There are no instances of the Exch\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETF 4 ;\r
+\r
+AE001:\r
+ MOV EDX,[EBP+0CH] ; Get the pExchRet in EDX\r
+ MOV [EDX],ESI ; Put Index of Exch at pExchRet\r
+ MOV EDX,pRunTSS ; Get pRunTSS in EDX\r
+ MOV EAX,[EDX+TSS_pJCB] ; Get the pJCB in EAX\r
+ MOV [EBX+Owner],EAX ; Make the Exch owner the Job\r
+ STI ;\r
+ MOV DWORD PTR [EBX+EHead],0 ; Make the msg/TSS queue NIL\r
+ MOV DWORD PTR [EBX+ETail],0 ;\r
+ DEC _nEXCHLeft ; Stats\r
+ XOR EAX,EAX ;ercOK (0)\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETF 4 ;\r
+\r
+;=============================================================================\r
+;\r
+; DeAllocExch - The kernel DeAllocate Exchange primitive. It allows a TASK\r
+; to deallocate a "message port." It also deQueues any messages, and frees\r
+; up any Link Blocks, TSSs, and RQBs, attached to the exchange\r
+;\r
+; Procedural Interface :\r
+;\r
+; DeAllocExch(Exch):ercType\r
+;\r
+; Exch is the Exchange Handle the process is asking to be released.\r
+\r
+PUBLIC __DeAllocExch: ;\r
+ PUSH EBP ;\r
+ MOV EBP,ESP ;\r
+\r
+ MOV ESI,[EBP+0CH] ; Load the Exchange Index in ESI\r
+ MOV EAX,ESI ; Get the Exchange Index in EAX\r
+ MOV EDX,sEXCH ; Compute offset of Exch in rgExch\r
+ MUL EDX ;\r
+ MOV EDX,prgExch ; Add offset of rgExch => EAX\r
+ ADD EAX,EDX ;\r
+ MOV ECX,EAX ; Make a copy in ECX (ECX = pExch)\r
+\r
+ MOV EDX,pRunTSS ; Get the pRunTSS in EDX\r
+ MOV EBX,[EDX+TSS_pJCB] ; Get pJCB in EBX\r
+ MOV EDX,[EAX+Owner] ; Get the Exchange Owner in EDX\r
+ CMP EBX,EDX ; If the CurrProc owns the Exchange,\r
+ JE DE000 ; yes\r
+ CMP EBX, OFFSET MonJCB ; if not owner, is this the OS???\r
+ JE DE000 ; yes\r
+ MOV EAX,ercNotOwner ;\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETF 4 ;\r
+\r
+DE000:\r
+ CLI ;\r
+ CMP DWORD PTR [ECX+fEMsg],0 ; See if a message may be queued\r
+ JE DE001 ; No. Go check for Task (TSS)\r
+ MOV ESI, ECX ; ESI must point to Exch for deQueue\r
+ CALL deQueueMsg ; Yes, Get the message off of the Exchange\r
+ OR EAX, EAX\r
+ JZ DE002 ; Nothing there. Go free the Exch.\r
+\r
+ ;Return the LB to the pool\r
+ MOV EBX,pFreeLB ; pLBin^.Next <= pFreeLB;\r
+ MOV [EAX+NextLB],EBX ;\r
+ MOV pFreeLB,EAX ; pFreeLB <= pLBin;\r
+ INC _nLBLeft ;\r
+ JMP DE000 ; Go And Check for more.\r
+\r
+ ; If we find an RqBlk on the exchange we must respond\r
+ ;with ErcInvalidExch before we continue! This will\r
+ ;only happen if a system service writer doesn't follow\r
+ ;instructions or a service crashes!\r
+ ;\r
+DE001:\r
+ CMP DWORD PTR [ECX+EHead], 0 ; Check to See if TSS is queued\r
+ JE DE002 ; NIL = Empty, JUMP\r
+ MOV ESI, ECX ; ESI must point to Exch for deQueue\r
+ CALL deQueueTSS ; Get the TSS off of the Exchange\r
+\r
+ ;Free up the TSS (add it to the free list)\r
+ MOV EBX,pFreeTSS ; pTSSin^.Next <= pFreeTSS;\r
+ MOV [EAX+NextTSS],EBX ;\r
+ MOV DWORD PTR [EAX+TSS_pJCB], 0 ; Make TSS invalid\r
+ MOV pFreeTSS,EAX ; pFreeTSS <= pTSSin;\r
+ INC _nTSSLeft ;\r
+\r
+ JMP DE001 ; Go And Check for more.\r
+DE002:\r
+ MOV DWORD PTR [ECX+Owner], 0 ; Free up the exchange.\r
+ MOV DWORD PTR [ECX+fEMsg], 0 ; Reset msg Flag.\r
+ INC _nEXCHLeft ; Stats\r
+ STI ;\r
+ XOR EAX,EAX ;ercOK (0)\r
+ MOV ESP,EBP ;\r
+ POP EBP ;\r
+ RETF 4 ;\r
+\r
+;=============================================================================\r
+;\r
+; GetTSSExch - This returns the Exchange of the current TSS to the\r
+; caller. This is primarily provided for System Services that provide\r
+; direct access blocking calls for customers.\r
+;\r
+; Procedural Interface :\r
+;\r
+; GetTSSExch(pExchRet):dError\r
+;\r
+; pExchRet is a pointer to where you want the Exchange Handle\r
+; returned. The Exchange is a DWORD (4 BYTES).\r
+;\r
+PUBLIC __GetTSSExch: ;\r
+ PUSH EBP ;\r
+ MOV EBP,ESP ;\r
+ MOV EAX,pRunTSS ; Get the Ptr To the Running TSS\r
+ MOV ESI,[EBP+0CH] ; Get the pExchRet in EDX\r
+ MOV EBX, [EAX+TSS_Exch] ; Get Exch in EBX\r
+ MOV [ESI],EBX ; Put Index of Exch at pExchRet\r
+ XOR EAX, EAX ; ErcOK\r
+ POP EBP ;\r
+ RETF 4 ;\r
+\r
+;=============================================================================\r
+;\r
+; SetPriority - This sets the priority of the task that called it\r
+; to the priority specified in the single parameter.\r
+;\r
+; Procedural Interface :\r
+;\r
+; SetPriority(bPriority):dError\r
+;\r
+; bPriority is a byte with the new priority.\r
+;\r
+PUBLIC __SetPriority ;\r
+ PUSH EBP ;\r
+ MOV EBP,ESP ;\r
+ MOV EAX,pRunTSS ; Get the Ptr To the Running TSS\r
+ MOV EBX,[EBP+0CH] ; Get the new pri into EBX\r
+ AND EBX, 01Fh ; Nothing higher than 31!\r
+ MOV BYTE PTR [EAX+Priority], BL ;Put it in the TSS\r
+ XOR EAX, EAX ; ErcOK - No error.\r
+ POP EBP ;\r
+ RETF 4 ;\r
+\r
+;=============================================================================\r
+;\r
+; GetPriority - This gets the priority of the task that called it\r
+; and passes it to bPriorityRet.\r
+;\r
+; Procedural Interface :\r
+;\r
+; SetPriority(bPriorityRet):dError\r
+;\r
+; bPriorityret is a pointer to a byte where you want the\r
+; priority returned.\r
+;\r
+PUBLIC __GetPriority ;\r
+ PUSH EBP ;\r
+ MOV EBP,ESP ;\r
+ MOV EAX,pRunTSS ; Get the Ptr To the Running TSS\r
+ MOV EBX,[EBP+0CH] ; Get the return pointer into EBX\r
+ MOV DL, BYTE PTR [EAX+Priority]\r
+ MOV BYTE PTR [EBX], DL ;\r
+ XOR EAX, EAX ; ErcOK - No error.\r
+ POP EBP ;\r
+ RETF 4 ;\r
+\r
+;======================== End of Module ======================\r