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