windows-nt/Source/XPSP1/NT/base/mvdm/wow16/user/winq.asm

1155 lines
33 KiB
NASM
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
;;;;;;;;;;;;;;;;;;;;;; START OF SOURCE FILE SPECIFICATION ;;;;;;;;;;;;;;;;;;;;
COMMENT $ HEADER
SOURCE FILE NAME: winq
DESCRIPTIVE NAME: queue management module
FUNCTION: This module contains routines for creating and
deleting queues and reading and writing messages
to queues. The circular queue data structure (struct Q
defined in user.h) consists of a header followed by a
sequence of messages
ENTRY POINTS: InitSysQ, CreateQueue, DeleteQueue, WriteMessage, ReadMessage,
FQueueNotFull, DelQEntry, UnlinkQ
$
;;;;;;;;;;;;;;;;;;;;;; END OF SOURCE FILE SPECIFICATION ;;;;;;;;;;;;;;;;;;;;
;% MOVEDS - NK (no change)
norasterops = 1
notext = 1
.xlist
include user.inc
.list
ExternFP <LocalAlloc, LocalFree>
createSeg _TEXT,CODE,WORD,PUBLIC,CODE
createSeg _DATA,DATA,WORD,PUBLIC,DATA,DGROUP
defgrp DGROUP,DATA
assumes cs,CODE
assumes ds,DATA
;============================================================================
sBegin DATA
ifndef WOW
ExternW idSysPeek ; id in sys queue of message being looked at.
endif
sEnd DATA
;============================================================================
ExternFP <GetSystemMsecCount>
ExternFP <GetCurrentTask>
ExternFP <SetTaskQueue>
ExternFP <GlobalAlloc, GlobalFree>
ExternFP <PostEvent>
ExternFP <PostMessage>
ExternFP <GetExeVersion>
sBegin CODE
; CS variables:
ifndef WOW ; WOW doesn't use these
ExternNP <CheckMsgFilter2>
ExternNP <CheckHwndFilter2>
ExternNP <SetWakeBit>
ExternNP <WakeSomeone>
ExternNP <SetWakeBit2>
ExternNP <SkipSysMsg>
ExternNP <PostMove>
ExternNP <HqCur2ES, HqCur2DS>
ExternFP <HqCurrent>
else
ExternFP <GetTaskQueueES>
endif ; WOW doesn't use these
ifdef WOW ; These functions stolen from winloop3.asm
;*--------------------------------------------------------------------------*
;* *
;* HqCur2ES() - *
;* *
;*--------------------------------------------------------------------------*
; Get hqCurrent in ES.
LabelNP <PUBLIC, HqCur2ES>
call GetTaskQueueES ; Another wonderful KERNEL routine
ret
;*--------------------------------------------------------------------------*
;* *
;* HqCurrent() - *
;* *
;*--------------------------------------------------------------------------*
; Get handle of current queue and return in AX.
LabelFP <PUBLIC, HqCurrent>
call HqCur2ES ; Code depends on both ES and AX
mov ax,es
or ax,ax ; Set flags
retf
endif ; WOW These functions stolen from winloop3.asm
ifndef WOW ; No InitSysQueue for WOW
;;;;;;;;;;;;;;;;;;;;;;;;;; START OF SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;
COMMENT $ LOCAL
InitSysQueue () - Create and initialize the system queue.
LINKAGE: FAR PLM
ENTRY: WORD cQEntries - number of entries in system queue
EXIT: hSysQueue contains handle for system queue (Shared global object.)
EFFECTS: all registers modified.
INTERNAL: CreateQueue2
EXTERNAL: none
WARNINGS: none
REVISION HISTORY:
IMPLEMENTATION:
Set up register linkage for CreateQueue2 and let it do the work.
$
;;;;;;;;;;;;;;;;;;;;;;;;;; END OF SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
cProc InitSysQueue,<PUBLIC,FAR>,<DS>
cBegin
mov ax,_INTDS
mov ds,ax
assumes ds,INTDS
mov ax,ds:[cQEntries] ; number of entries
push ax
mov ax,size INTERNALSYSMSG ; size of entry
push ax
call CreateQueue2 ; create system queue
mov ds:[hqSysQueue],ax
assumes ds,NOTHING
cEnd
endif ; No InitSysQueue for WOW
;;;;;;;;;;;;;;;;;;;;;;;;;; START OF SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;
COMMENT $ LOCAL
CreateQueue (cMsgs) - Create a queue.
Create a queue for the currently executing task and stick its handle
in the task header and the queue list.
LINKAGE: FAR PLM
ENTRY: WORD cMsgs(parm1) - count of messages that can be stored in
queue
EXIT: ax - handle to queue (shared global object.)
Newly created queue is linked to list of queues pointed
to by hqList.
EXIT - ERROR: ax hqCurrent, hqCurrentShadow contain 0.
EFFECTS: all registers modified.
INTERNAL: CreateQueue2
EXTERNAL: SetTaskQueue
WARNINGS: Running out of memory when trying to CreateQueue will
init current queue to 0.
REVISION HISTORY:
IMPLEMENTATION:
Set up register linkage for CreateQueue2 and let it create the queue.
Then add to linked list and then SetTaskQueue(NULL, hqCreated).
$
;;;;;;;;;;;;;;;;;;;;;;;;;; END OF SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
cProc CreateQueue,<PUBLIC, FAR>,<DS>
ParmW cMsgs
cBegin
push cMsgs
mov ax,size INTERNALMSG
push ax
call CreateQueue2
or ax,ax
jz errexit ; CreateQueue2 failed
EnterCrit
mov es,ax
push ax
mov ax,_INTDS
mov ds,ax
pop ax
assumes ds,INTDS
xchg ds:[hqList],ax ; Link us in and get old head of list
mov es:[qHqNext],ax ; store ptr to next queue
push es ; save hq for return
xor ax,ax ; SetTaskQueue(NULL, es)
push ax
push es ; push queue handle
call SetTaskQueue ; and ram it in there
pop ax ; return queue handle
LeaveCrit
assumes ds,DATA
errexit:
cEnd
;;;;;;;;;;;;;;;;;;;;;;;;;; START OF SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;
COMMENT $ PRIVATE
CreateQueue2 (cMsgs, cbEntry) - Create a queue.
Allocate a shared global object and initialize the header for the Q
Data structure.
LINKAGE: NEAR PLM
ENTRY: WORD cMsgs(parm1) - count of messages that can be stored in
queue
WORD cbEntry(parm2) - count of bytes in single message entry.
EXIT: ax - handle to queue (shared global object.)
EXIT - ERROR: ax contains 0
EFFECTS: all registers except DI modified.
wAppVersion gets current exe version.
INTERNAL:
EXTERNAL: GlobalAlloc
WARNINGS: cbEntry better not be less than 5.
REVISION HISTORY:
IMPLEMENTATION:
Allocate a shared global object, initialize header with following
fields: Current Task, cbEntry, cMsgs,pmsgRead, pmsgWrite,
pmsgRead = pmsgWrite = rgMsg,
pmsgMax = queue size
wVersion = GetExeVersion
WakeBits = QS_SMPARAMSFREE | (SYSQ empty ? QS_INPUT : 0).
lpfnMsgFilter = cs:OldMsgFilter
$
;;;;;;;;;;;;;;;;;;;;;;;;;; END OF SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
cProc CreateQueue2,<PUBLIC, NEAR>,<DI>
ParmW cMsgs
ParmW cbEntry
cBegin
call GetCurrentTask ; get current task
push ax ; and save it
mov ax,cMsgs
mov cx,cbEntry
mul cx
add ax,size Q - size INTERNALMSG
push ax ; save size of queue
; Alloc(GPTR, cMsgs * size INTERNALMSG + size Q)
push GPTR+GMEM_SHAREALL
push 0 ; hi word == 0
push ax
call GlobalAlloc
mov es,ax ; stick hq in es
pop cx ; pop queue size
pop bx ; and task handle
or ax,ax ; error on alloc?
jz cqexit ; yes, quit
xchg bx,ax ; get task handle into ax
cld
mov di,qHTask ; point at hTask
stosw ; store task handle
errnz qHTask-2
mov ax,cbEntry ; store size of entry
stosw
errnz qCbEntry-4
inc di ; cMsgs = 0 (cleared by alloc)
inc di
errnz qCMsgs-6
mov ax,qRgmsg
stosw ; init read/write pointers
errnz qPmsgRead-8
stosw
errnz qPmsgWrite-10
mov ax,cx ; get size of queue (ptr to end)
stosw ; and store it
errnz qPmsgMax-12
push es ; save hq
call GetExeVersion ; returns sys version in dx, app in ax
pop es
mov es:[qWVersion],ax ; set up app version number
mov es:[qWakeBits],QS_SMPARAMSFREE ; default ON flags.
mov es:[qFlags],QF_INIT ; indicate initialization is in progress
mov ax,es ; Save hq in ax.
push ax
mov ax,_INTDS
mov es,ax
pop ax
assumes es,INTDS
cmp es:[hqList],0 ; If we are the first queue and
jnz cq100 ; there is input waiting for us,
mov es:[hqCursor],ax ; Initialize this guy for WakeSomeone.
cmp es:[hqSysQueue],0 ; System queue set yet???
jz cq100 ; Nope, don't touch it
mov bx,es:[hqSysQueue] ; set the input flag.
or bx,bx
jz cq100
mov es,bx
cmp es:[qCMsgs],0 ; Any messages in the system queue?
mov es,ax
jz cq100 ; No messages.
or es:[qWakeBits],QS_INPUT ; Yes - tell the guy he has input.
cq100:
;
; Return queue handle in ax.
;
cqexit:
cEnd
;============================================================================
;;;;;;;;;;;;;;;;;;;;;;;;;; START OF SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;
COMMENT $ LOCAL
DeleteQueue() - Remove current queue from queue list
Unlink Queue from all lists, Delete the Q global object, then wake
someone else.
LINKAGE: NEAR PLM
ENTRY:
EFFECTS: current queue is removed from queue list and object is deleted.
New task wakes up.
hqSysLock = 0
all registers modified.
INTERNAL: UnlinkQ, SetWakeBit, WakeSomeone
EXTERNAL: GlobalFree,
WARNINGS: none
REVISION HISTORY:
IMPLEMENTATION:
Unlink the queue from list of queues then unlink from all send lists.
Then set the result bit for every queue with sendmessage waiting on
this guy. Then free queue global object, and wakesomeone.
$
;;;;;;;;;;;;;;;;;;;;;;;;;; END OF SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
cProc DeleteQueue,<PUBLIC, NEAR>, <SI>
cBegin
call HqCurrent
mov si,ax ; si = hqCurrent.
mov dx,_INTDS
push dx ; Save for later
mov ax,OFFSET hqList
push dx
push ax
push si
mov ax,qHqNext
push ax
call UnlinkQ ; UnlinkQ(lphqStart, hqCurrent, cbHqNext)
pop es ; get INTDS
jz dqexit ; if bad unlink, exit.
ifndef WOW ; WOW doesn't have most USER16 structures to worry about.
assumes es,INTDS
EnterCrit
xor bx,bx ; zero hqSysLock.
mov es:[hqSysLock],bx
mov es:[hqMouse],bx ; zero hqMouse.
mov es:[hqKeyboard],bx ; zero hqKeyboard.
mov bx,es:[hqList] ; Get the first guy in the list for
mov es:[hqCursor],bx ; hqCursor.
LeaveCrit
;
; Unlink this guy from everyone's hqSendList.
;
mov cx,es:[hqList]
assumes es,NOTHING
dq100:
mov es,cx
jcxz dq200
push es:[qHqNext]
push cx ; Unlink this queue from all send
mov ax,qHqSendList ; lists.
push ax
push si
mov ax,qHqSendNext
push ax ; UnlinkQ(lphqStart, hqUnlink, cbLink)
call UnlinkQ
pop cx ; Get the next hq.
jmps dq100
dq200:
;
; Now set the result bit of everyone waiting on a SendMsg response from
; this guy.
;
mov es,si ; es = hqCurrent.
mov cx,es:[qHqSendList]
dq300:
mov es,cx
jcxz dq400 ; Are we at the end of the list?
push es:[qHqSendNext]
xor bx,bx
mov word ptr es:[qResult],bx ; Zero out the result.
mov word ptr es:[qResult+2],bx
mov ax,QS_SMRESULT ; Tell this guy to wake up and
call SetWakeBit2 ; get the result.
pop cx
jmps dq300
endif ; WOW doesn't have most USER16 structures to worry about.
dq400:
xor ax,ax
push ax
push ax ; Set NULL queue
call SetTaskQueue ; SetTaskQueue(NULL, NULL)
push si
call GlobalFree ; throw away the queue
xor cx,cx
ifndef WOW ; No need - user32 takes care of this for WOW
call WakeSomeone ; wake someone up to process events
endif ;WOW
; if anyone jumps here, he better do it with ints enabled!
dqexit:
cEnd
ifndef WOW ; WOW doesn't ever put anything in the 16-bit Queue
;;;;;;;;;;;;;;;;;;;;;;;;;; START OF SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;
COMMENT $ LOCAL
WriteMessage (hq, lParam, wParam, message, hwnd, dwExtra) - Write a message
to hq.
If queue not full, Write message record at pmsgWrite, then advance pmsgWrite.
SetWakeBit(hq) to tell him he has input.
LINKAGE: NEAR PLM
ENTRY: WORD hq handle to queue that gets message
DWORD lParam lParam of message
WORD wParam wParam of message
WORD message message
WORD hwnd associated window
DWORD dwExtra dwExtraMsgInfo of the message
EXIT: zero flag not set
AX = pmsgWrite
EXIT ERROR: zero flag set
EFFECTS: Write pointer (pmsgWrite) to hq advanced to next message.
QS_POSTMESSAGE set for hq.
All registers changed
INTERNAL: FQueueNotFull, SetWakeBit2
EXTERNAL: none
WARNINGS: the order of params on stack is assumed so we can do
a rep movsb
REVISION HISTORY:
IMPLEMENTATION:
Call FQueueNotFull which sets di = pmsgwrite if queue not full.
Blt message bytes to queue at di, advance the write pointer,
then SetWakeBit(hq, QS_POSTMESSAGE).
$
;;;;;;;;;;;;;;;;;;;;;;;;;; END OF SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
cProc WriteMessage,<PUBLIC, NEAR>, <SI,DI,DS>
ParmW hq
ParmD lParam
ParmW wParam
ParmW message
ParmW hwnd
ParmD dwExtraInfo
cBegin WriteMessage
NewEnterCrit ax ; This trashes ax register
push ds ; See if queue is full.
mov ax,hq
mov ds,ax
mov es,ax
assumes ds,NOTHING
assumes es,NOTHING
call FQueueNotFull ; Z flag set if FULL.
jz pmexit
push di ; Save Message pointer.
pm30:
mov cx,ds:[qCbEntry] ; Copy message into queue.
shr cx,1
push ds
lea si,dwExtraInfo
push ss
pop ds
cld
errnz <size INTERNALSYSMSG - 7*2>
errnz msgTime-10
errnz imMsg-4
movsw
movsw
movsw
movsw
movsw ; Store away the message.
movsw
movsw
errnz <size INTERNALSYSMSG - 7*2>
errnz msgTime-10
errnz imMsg-4
sub cx,7
jcxz pm40 ; If have room, store time.
push es
call GetSystemMsecCount ; Tick count in dx:ax.
pop es
push ax
mov ax,_INTDS
mov ds,ax
pop ax
assumes ds,INTDS
stosw
mov ax,dx
stosw
sub cx,2
jcxz pm40
mov ax,word ptr ds:[ptCursor] ; If have room, store pt.
stosw
mov ax,word ptr ds:[ptCursor+2]
stosw
pm40:
pop ds
assumes ds,DATA
pop bx ; reget ptr to msg
push bx
call rtestwrap ; advance pointer
mov ds:[qPmsgWrite],bx
inc ds:[qCMsgs] ; advance count
mov ax,QS_POSTMESSAGE
call SetWakeBit2 ; Tell this guy he has input.
pm75:
pop ax ; get back message ID
pmexit:
pop ds
NewLeaveCrit dx, cx ; This trashes dx and cx registers
cEnd WriteMessage
;;;;;;;;;;;;;;;;;;;;;;;;;; START OF SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;
COMMENT $ LOCAL
FQueueNotFull - IsQueueFull?
If queue not full, get pmsgWrite and return TRUE.
otherwise return FALSE.
LINKAGE: register
ENTRY: WORD ax - hq
EXIT: ax - non zero. zero flag clear
di - pmsgWrite (pointer to next write record.)
EXIT ERROR: ax - 0; zero flag set
EFFECTS: no other registers
INTERNAL: none
EXTERNAL: none
WARNINGS: none
REVISION HISTORY:
IMPLEMENTATION:
Advance write pointer. Error condition occurs only if
pmsgWrite == pmsgRead && cMsg != 0.
$
;;;;;;;;;;;;;;;;;;;;;;;;;; END OF SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; FQueueNotFull - zero in ax and Z flag if FULL, else cMsgLeft in ax, NZ
; flag set. Returns write pointer in di.
; AX has DS on entry.
;
LabelNP <PUBLIC, FQueueNotFull>
push ds
assumes ds,NOTHING
mov ds,ax
mov di,ds:[qPmsgWrite] ; get write pointer and advance
cmp di,ds:[qPmsgRead] ; if read == write, then we're either
jnz qfNotFull
xor ax,ax
cmp ax,ds:[qCMsgs] ; cMsgs != 0 if empty
jnz qfexit ; Jump if Full.
qfNotFull:
push es
mov ax,_INTDS
mov es,ax
assumes es,INTDS
mov ax,es:[cQEntries] ; See how many messages are left.
assumes es,NOTHING
pop es
sub ax,ds:[qCMsgs] ; number of messages left
qfexit:
or ax,ax
pop ds
assumes ds,DATA
ret
;;;;;;;;;;;;;;;;;;;;;;;;;; START OF SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;
COMMENT $ LOCAL
ReadMessage (hq, lpMsg, hwndFilter, msgMinFilter, msgMaxFilter, fRemoveMsg)
Read a message from hq.
If queue not empty, read message satisfying filter conditions from hq to *lpMsg.
LINKAGE: FAR PLM
ENTRY: WORD hq handle to queue to read from
MSG *lpMsg far pointer to message buffer
NOTE: This points to MSG struct and not INTERNALMSG.
WORD hwndFilter Window filter
WORD msgMinFilter min filter spec for message number
WORD msgMaxFilter max filter spec for message number
WORD fRemoveMsg Remove message if non-zero
EXIT: zero flag not set
ax = Non-Zero - we have a message.
ax = 0 - we don't have a message.
EXIT ERROR: zero flag set
EFFECTS: All registers trashed
QS_POSTMESSAGE wakebit cleared if no msgs left in app queue.
INTERNAL: none
EXTERNAL: none
WARNINGS:
REVISION HISTORY:
SRL - 4/12 Fixed bug where if system queue was locked by someone else,
it was being unlocked.
SRL - 4/18 Fixed journalling.
SRL - 5/4 Changed system queue journalling to call ScanSysQueue.
(which used to be called FindMsgHq. It now does all the
message enumeration).
IMPLEMENTATION:
If not quitting, look through the specified queue starting at pmsgRead
for message that matches filters. Blt message to lpmsg, advance the read
pointer. If out of queue messages, clear the input bit.
Message matches hwndFilter if hwndFilter == 0 or hwndFilter == msgHwnd.
Message matches msgMinFilter, msgMaxFilter if both are zero or
msgMinFilter <= msg <= msgMaxFilter.
$
;;;;;;;;;;;;;;;;;;;;;;;;;; END OF SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; ReadMessage(hq, lpMsg, hwndFilter, msgMinFilter, msgMaxFilter, fRemoveMsg)
;
; NOTE: lpMsg points to MSG structure and not INTERNALMSG.
;
cProc ReadMessage, <PUBLIC, FAR>, <SI, DI,DS>
ParmW hq
ParmD lpMsg
ParmW hwndFilter
ParmW msgMinFilter
ParmW msgMaxFilter
ParmW fRemoveMsg
cBegin
push hq ; set ds to queue pointer
pop ds
assumes ds,NOTHING
mov bx,fRemoveMsg ; pass this to rmquit
call rmquit ; Must we quit?
jnz rmexit ; Exit and be sure to leave the
; QS_POSTMESSAGE bit still set.
;
; Run through the queue and find a message that matches the filters.
;
rm150:
xor ax,ax
cmp ds:[qCMsgs],ax
jz rm500 ; nothing in queue: exit
mov si,ds:[qPmsgRead] ; Get current read pointer.
rm200:
call rmcheckappqueue ; Check the app queue for a message
jnz rm500 ; fitting the filters.
rm400:
call rtestwrap ; increment pointer in bx and copy to si
mov si,bx
jnz rm200 ; at end of queue if Z set
xor ax,ax ; return FALSE
rm500:
EnterCrit
cmp ds:[qCMsgs],0 ; If no messages left, and out the
jnz rm600 ; input bit.
cmp ds:[qCQuit],0 ; But only if not quitting
jnz rm600
and ds:[qWakeBits],NOT QS_POSTMESSAGE
rm600:
LeaveCrit
rmexit:
assumes es,NOTHING
assumes ds,DATA
cEnd
assumes ds,NOTHING
; bx = fRemoveMsg
rmquit:
mov ax,ds:[qCQuit] ; Are we in the middle of quiting?
or ax,ax ; if cQuit == 0, then continue
jz rmq200 ; return Z.
; dec al ; if al == 2, then send quit msg
; jnz rmq100
cmp ds:[qCMsgs],0 ; if queue not empty, don't send quit
jnz rmq200
; inc ds:[qCQuit] ; set sticky quit & send quit msg
or bx,bx
jz rmq100 ; if PM_NOREMOVE, multiple WM_QUIT's
mov ds:[qCQuit],0 ; give only one WM_QUIT(raor)
rmq100:
les bx,lpMsg
mov es:[bx].msgMessage,WM_QUIT ; send a WM_QUIT
mov es:[bx].msgHwnd,NULL ; null window handle
mov ax,ds:[qExitCode] ; stick the exit code in wParam
mov es:[bx].msgWParam,ax
or al,TRUE
ret
rmq200:
xor ax,ax
ret
;
; If the hq is an app queue, we can filter immediately.
;
rmcheckappqueue:
mov bx,ds:[si].ismMsg.msgHwnd ; see if hwnd satisfies hwndFilter
mov cx,hwndFilter
call CheckHwndFilter2
mov bx,si ; copy read ptr into bx
jz rmc200 ; bad luck - try next one
mov ax,ds:[si].ismMsg.msgMessage ; see if msgMinFilter <= msg <= msgMaxFilter
mov cx,msgMinFilter ; cx = msgMinFilter
mov dx,msgMaxFilter ; dx = msgMaxFilter
call CheckMsgFilter2 ; Z if no message.
jz rmc200
;
; We've found a message -- read it into lpMsg
;
les di,lpMsg ; get pointer to event block
mov cx,ds:[qCbEntry]
sub cx, size INTERNALMSG - size MSG
push si ; preserve ptr to ExtraMsgInfo
add si, size INTERNALMSG - size MSG
cld
rep movsb ; copy message structure & advance rd ptr
;
; save away time, id, and position of this event in queue header
;
; NOTE: this code doesn't work for the system queue, but that's ok since we
; don't use the queue's time anyway.
;
sub si,size MSG - msgTime ; point at saved time
mov di,qTimeLast ;
push ds ; copy into ES too
pop es
movsw ; copy time and position
movsw
errnz qPtLast-qTimeLast-4
movsw
movsw
mov ax,bx ; store position of msg in q header
stosw
errnz qIdLast-qPtLast-4
pop si ; Restore ptr to ExtraMsgInfo
errnz qdwExtraInfoLast-qIdLast-2
errnz <size INTERNALMSG - size MSG - 4>
movsw
movsw
cmp fRemoveMsg,0 ; are we supposed to yank the message?
jnz rmc100 ; yes: take it out
mov ds:[qIdLast],1 ; stick something random in qidLast
jmps rmc150 ; so we don't reply until it's yanked
rmc100:
call DelQEntry ; delete queue entry & update ptrs
rmc150:
mov ax,bx ; return ID value, TRUE, NZ.
or ax,ax
rmc200:
ret
assumes ds,DATA
;;;;;;;;;;;;;;;;;;;;;;;;;; START OF SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;
COMMENT $ PRIVATE
DelQEntry - Delete a queue message entry.
Delete message entry from queue. Adjust pmsgRead, pmsgWrite
and cMsgs.
LINKAGE: register
ENTRY: WORD bx - pointer to entry to delete
WORD ds - hq
EXIT: void
EFFECTS: pmsgRead and pmsgWrite are adjusted, and part of rgMsg
is blt'ed to fill hole left by deleted message.
Trashes es, si, di
INTERNAL: none
EXTERNAL: none
WARNINGS: none
REVISION HISTORY:
IMPLEMENTATION:
disable interrupts, change cMsgs, pmsgRead, pmsgWrite, then
rep movsb from bottom up.
$
;;;;;;;;;;;;;;;;;;;;;;;;;; END OF SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LabelNP <PUBLIC, DelQEntry>
assumes DS,NOTHING
EnterCrit
push ds
pop es
dec ds:[qCMsgs] ; decrement count
mov ax,ds:[qCbEntry]
mov si,ds:[qPmsgRead]
mov di,ds:[qPmsgWrite]
cmp si,bx ; compare sptr to dptr
ja de500
;
; rptr < dptr: blt stuff below up
; move(rptr, rptr+1, dptr-rptr)
; rptr++;
;
std ; blt backwards
mov cx,bx ; cb = dptr-rptr
sub cx,si
mov si,bx ; src = dptr
dec si ; point at first byte to copy
mov di,si ; dest = src + size(entry)
add di,ax
rep movsb ; copy those bytes
inc di ; readjust DI
cmp di,ds:[qPmsgMax] ; update read pointer
jb de200 ; checking for wraparound
mov di,qRgmsg
de200:
mov ds:[qPmsgRead],di
cld
LeaveCrit
ret
;
; rptr > dptr: blt stuff above down
; wptr--;
; move(dptr + 1, dptr, wptr - dptr)
;
de500:
cld
mov si,bx ; src = dptr + size(entry)
add si,ax
mov cx,di ; cb = (wptr - (dptr + size(entry)))
sub cx,si
mov di,bx ; dest = dptr
rep movsb ; copy those bytes
mov ds:[qPmsgWrite],di ; update write pointer
LeaveCrit
ret
;
; increment read ptr in bx, testing for wraparound
;
LabelNP <PUBLIC, rtestwrap>
add bx,ds:[qCbEntry] ; advance pointer
cmp bx,ds:[qPmsgMax] ; wrap around if needed
jb tw50
mov bx,qRgmsg
tw50:
cmp bx,ds:[qPmsgWrite] ; set CC's if end of queue
ret
assumes ds,DATA
endif ; WOW doesn't ever put anything in the 16-bit Queue
;;;;;;;;;;;;;;;;;;;;;;;;;; START OF SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;
COMMENT $ LOCAL
LinkQ (lphqStart, hqLink, ibLink) - Add Q to linked list.
Add new queue entry to END of linked list starting at lphqStart and
linked by ibLink. ibLink can be qHqNext or qHqSendNext.
LINKAGE: NEAR PLM
ENTRY: DWORD lphqStart (parm1) - far pointer to start of queue list
WORD hqLink (parm2) - handle of queue to be linked
WORD ibLink (parm3) - byte index to link to be used in
Q structure.
EXIT: ax contains hqLink
EFFECTS: all registers modified.
INTERNAL: none
EXTERNAL: none
WARNINGS: none
REVISION HISTORY:
IMPLEMENTATION:
Walk the linked list starting at lphqStart till hqNext is NULL indicating
end of list. Add new entry to list there.
$
;;;;;;;;;;;;;;;;;;;;;;;;;; END OF SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LabelNP <PUBLIC, LinkQ>
; ret = 0+2 (saved si)
; ibLink = 2+2
; hqLink = 4+2
; lpHqStart = 6+2
;
push si
mov si,sp
EnterCrit
les bx,ss:[si+8] ; Get lpHqStart
lnk100:
mov cx,word ptr es:[bx] ; Check for end of the list.
jcxz lnk200
mov es,cx ; Get Next Link.
mov bx,ss:[si+4] ; BX = ibLink.
jmps lnk100
lnk200:
mov ax,ss:[si+6] ; Store hqLink at end of list.
mov word ptr es:[bx],ax
mov es,ax ; AX = hqLink.
mov bx,ss:[si+4]
mov word ptr es:[bx],cx ; CX = 0. Zero out pNext.
LeaveCrit
pop si
ret 8
;;;;;;;;;;;;;;;;;;;;;;;;;; START OF SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;
COMMENT $ LOCAL
UnlinkQ (lphqStart, hqLink, ibLink) - Unlink Q from list.
Unlink queue specified by hqLink from linked list starting at lphqStart and
linked by ibLink. ibLink can be qHqNext or qHqSendNext.
LINKAGE: NEAR PLM
ENTRY: DWORD lphqStart (parm1) - far pointer to start of queue list
WORD hqLink (parm2) - handle of queue to be unlinked
WORD ibLink (parm3) - byte index to link to be used in
Q structure.
EXIT: ax contains handle of unlinked queue, zero flag clear
EXIT ERROR - zero flag is set
EFFECTS: all registers modified.
INTERNAL: none
EXTERNAL: none
WARNINGS: none
REVISION HISTORY:
IMPLEMENTATION:
Walk the linked list starting at lphqStart till hqLink is found and
unlink.
$
;;;;;;;;;;;;;;;;;;;;;;;;;; END OF SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LabelNP <PUBLIC, UnlinkQ>
;
;
; ret = 0+2+ (pushed si)
; cbLink = 2+2
; hq = 4+2
; lpHqStart = 6+2
;
assumes ds,NOTHING
push si
mov si,sp
push di
push ds
EnterCrit
les di,ss:[si+8] ; Get lpHqStart.
mov bx,ss:[si+6] ; Get hqUnlink.
ulq100:
mov cx,es:[di] ; Exit with zero if at end of list.
jcxz ulqExit
cmp cx,bx ; Have we found the guy that points
jz ulq200 ; to hqUnlink?
mov es,cx ; Point to the next guy and continue.
mov di,ss:[si+4]
jmps ulq100
ulq200:
mov ds,bx ; Get the hq that hqUnlink points to.
mov bx,ss:[si+4]
mov bx,ds:[bx]
mov es:[di],bx ; Store in hqLinkNext of the guy
; previous to hqUnlink.
ulqExit:
LeaveCrit
or cx,cx
pop ds
pop di
pop si
ret 8
assumes DS,DATA
ifndef WOW ; WOW thunks SetMessageQueue
;;;;;;;;;;;;;;;;;;;;;;;;;; START OF SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;
COMMENT $ LOCAL
BOOL SetMessageQueue(cMsg)
This function is available to the task that desires a larger message
queue than the default. This routine must be called from the
applications' WinMain routine as soon as possible. It must not be
called after any operation that could generate messages (such as
a CreateWindow). Any messages currently in the queue will be
destroyed. By default, the system creates a default queue with room
for 8 messages.
LINKAGE: FAR PLM
ENTRY: WORD cMsg - The size of the new queue in messages.
EXIT: ax contains:
TRUE: If new queue is sucessfully created.
FALSE: If there was an error creating the new queue. In this case,
the application has no queue associated with it (since the
origional queue is deleted first). The application MUST
call SetMessageQueue with a smaller size until a TRUE is
returned (or the application may terminate itself).
EFFECTS: SI, DI preserved.
INTERNAL: none
EXTERNAL: none
WARNINGS: none
REVISION HISTORY:
IMPLEMENTATION:
Call DeleteQueue()
Call CreateQueue(cMsg)
$
;;;;;;;;;;;;;;;;;;;;;;;;;; END OF SPECIFICATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
cProc SetMessageQueue, <PUBLIC, FAR, EXPORTED>, <SI, DI>
ParmW cMsg
cBegin
call DeleteQueue
mov ax,cMsg
push ax
call CreateQueue
cEnd
endif ; WOW thunks SetMessageQueue
sEnd CODE
end