;       SCCSID = @(#)debsym.asm 6.1 90/11/17
NAME   DEBSYM
TITLE  Kernel Debugger Symbol Handler
.286p

SCCSID  equ     "@(#)debsym.asm 13.24 90/04/27"

;***    DEBSYM  -  Routines to support symbols in the debugger.
;
;
;       Author   Thomas Parslow (tomp)
;
;       Date Created    86.12.10
;       Last Edit       87.02.17
;
;
;       Subroutines
;
;       Debug_Sym       - Functional entry point for "link" related functions.
;       LinkMap         - Links a map into the current map chain.
;       ReLinkMap       - Substitutes new Sel/Seg values for linked map.
;       UnLinkMap       - Deletes the a linked map.
;
;       GetSymbolVal    - Finds a sym's value and type if defined.
;       GetSymbolBuf    - Finds a sym's name given its value.
;       GetAbsSymBuf    - Finds an absolute sym's name given its value.
;
;       ListMaps        - Prints names of currently linked maps
;       ListGroups      - Prints names of groups in currently active map.
;       ListNear        - Lists the nearest pre/post symbols for input address.
;       ListSymbols     - Prints symbols defined for specified group.
;       ListAbsSyms     - Prints the absolute symbols defined for active map.
;
;       WithMap         - Activate a particular, previously linked map.
;
;
;       Edit History
;
;       Thomas Parslow   87.02.12
;       Added routines to provide the "list near symbols" command.
;
;       Thomas Parslow   87.02.10
;       Added code to mask off RPL field during selector compares.
;
;       Mike McLaughlin  87.03.02
;       Merged with 1.1 debugger.
;
;       Sean McKinnon    87.10.09
;       Implemented Each*() routines to allow for >64K symbol tables.
;       Re-Wrote all the above routines to use Each*() interfaces.
;
;       Thomas Fenwick   88.08.25
;       Added code to support 32-bit maps.

DEBUG   equ     1

.xlist
include  dosseg.inc
include  basemaca.inc
include  fnodeb.inc
include  osmaca.inc
include  debequ.inc
include  debseg.inc
include  debsym.inc
include  debmac.inc
include  debdesc.inc
include  protmode.inc
.list

EACHMAP_ACTIVE          equ     0001h
EACHMAP_START_LAST      equ     0002h

MI_ADDRESSSIZE          equ     67H

;*      Make sure the public symbol type equates are the same
;       as the internal address type equates.

.errnz  SYM_TYPE_SEG-EXPR_TYPE_SEG
.errnz  SYM_TYPE_SEL-EXPR_TYPE_SEL
.errnz  SYM_TYPE_LIN-EXPR_TYPE_LIN
.errnz  SYM_TYPE_PHY-EXPR_TYPE_PHY

ReferFlat NESymHead,DWORD
EXTRN     CurrTask:WORD

DataSegmentStart

Extern  AtomBuf,        BYTE            ; String buffer,    in "debeval.asm".
Extern  DisAddr,        WORD            ; Disassembly address
Extern  RegSA,          WORD            ; Register save area
Extern  DefType,        WORD            ; Default address type
Extern  KDPhMTE,        WORD            ; DLL hMTE for notification
Extern  KDPpMTE,        DWORD           ; DLL pMTE for notification
Extern  fKDPPacketMode, BYTE            ; Packet Mode flag

;*      These data are statically initialized only; this way if the
;       debugger is reinitialized, the map list will not be lost.

addrsFirstMap   db      (size AddrS) DUP (0)    ; Beginning of Map chain
addrsLastMap    db      (size AddrS) DUP (0)    ; Last map symbol found
cMapCount       dw      0                       ; Number of maps

Static  ListTable, LABEL, BYTE
    CmdTable_s <"A", codeOFFSET ListAbsSyms>    ; List near symbols subcom.
    CmdTable_s <"G", codeOFFSET ListGroups>     ; List groups subcommand.
    CmdTable_s <"M", codeOFFSET ListMaps>       ; List maps subcommand.
    CmdTable_s <"N", codeOFFSET ListNear>       ; List absolute symbols subcom.
    CmdTable_s <"S", codeOFFSET ListSyms>       ; List symbols subcommand.
    CmdTable_s <0,   codeOFFSET perr>           ; Default command.

Static  LinkTable, LABEL, WORD
        dw      codeOFFSET LinkMap
        .errnz  SYM_LINKMAP-0
        dw      codeOFFSET ReLinkMap
        .errnz  SYM_RELINKMAP-1
        dw      codeOFFSET UnLinkMap
        .errnz  SYM_UNLINKMAP-2

SYM_TAB_LEN     equ     (($ - LinkTable) SHR 1)

WithMapMsg      label   byte
        db      "Use ""WA mapname | *"" to add a map to the active list"
        db      CR, LF
        db      "Use ""WR mapname | *"" to remove a map from the active list"
        db      CR, LF
        db      0

pMapDefCur      dw      ?       ; pointer into stack for current MapDef

DataSegmentEnd

CodeSegmentStart

Extern ExitDebug, NEAR
Extern perr, NEAR
Extern KDPEvent, NEAR

;***    KDPLink - Notify CVK that a module has been loaded
;
;       ENTRY:  (Client's EAX) = pMTE
;               (Client's CX) = hMTE
;
;       EXIT:
;
.386p
DefProc KDPLink, FAR, PUBLIC
        Pcall   EnterDebugRETF
        cmp     fKDPPacketMode,0
        je      short klkx

        mov     eax,RegSA.EAXsav
        mov     KDPpMTE,eax             ; Store pMTE
        mov     cx,RegSA.CXsav
        mov     KDPhMTE,cx              ; Store MTE handle
        mov     eax,KDP_LINK_EVENT
        jmp     KDPEvent

klkx:   jmp     ExitDebug
EndProc NoGen

;***    KDPUnlink - Notify CVK that a module has been unloaded
;
;       ENTRY:  (Client's EAX) = pMTE handle
;               (Client's CX) = hMTE
;
;       EXIT:
;

DefProc KDPUnlink, FAR, PUBLIC
        Pcall   EnterDebugRETF
        cmp     fKDPPacketMode,0

        je      short kulx

        mov     eax,RegSA.EAXsav
        mov     KDPpMTE,eax             ; Store pMTE
        mov     cx,RegSA.CXsav
        mov     KDPhMTE,cx              ; Store MTE handle
        mov     eax,KDP_UNLINK_EVENT
        jmp     KDPEvent

kulx:   jmp     ExitDebug
EndProc NoGen
.286p

;***    Debug_Sym32 - 32 bit entry for linking, unlinking, and relinking maps.
;***    Debug_Sym   - Entry for linking, unlinking, and relinking maps.
;
;       ENTRY:  (DS:(E)SI) = Pointer to paramter frame
;               (AL)    = Function number
;                       0 = LinkMap
;                       1 = ReLinkMap
;                       2 = UnLinkMap
;
;       EXIT:   Carry clear, if ok.
;               Carry set, if function failed.
;
;       USES:   Flags
;

DefProc Debug_Sym32, FAR, PUBLIC, FAR16
        Pcall   EnterDebugRETFD
        jmp     short dsy10

Entry   Debug_Sym, FAR, PUBLIC
        Pcall   EnterDebugRETF
        mov     RegSA.ESIsav.HighWord,0

dsy10:  mov     bx,RegSA.AXsav          ; Put num in an index reg.
        xor     bh,bh                   ; Since func number is in bl.

;       Check whether the requested function is valid

        cmp     bx,SYM_TAB_LEN          ; Is this a valid function?
        jae     short dsy20             ; Is an invalid call so exit.

;       Call the requested function

        shl     bx,1                    ; Make an offset into the table.
        call    LinkTable[bx]           ; Call the function requested.
        jc      short dsy20
        and     RegSA.Fsav,not F_CARRY
        jmp     ExitDebug

dsy20:  or      RegSA.Fsav,F_CARRY
        jmp     ExitDebug
EndProc NoGen

;***    LinkMap - Link a symbol map into the chain of maps in memory.
;
;       ENTRY:   Address of a parameter frame containing
;                the information depicted below.
;
;                Parameter frame:
;                        MapAddrS        db size AddrS DUP (?)
;                        Group Count     dw
;                        SegDef Type     dw -+
;                        SegDef Selector dw  +  X  Group Count
;                        SegDef Segmemt  dw  +
;                        Segment Number  dw -+
;
;       NOTE:   "SegDefType" can be one of the set [SYM_TYPE_(SEL,SEG)]
;               or SYM_TYPE_BIMODAL which indicates that both "SegDefSel" and
;               "SegDefSeg" are filled in for bimodal device drivers.
;               Otherwise "SegDefSeg" has the "EXPR_TYPE_SEG" value and
;               "SegDefSeg" has the "EXPR_TYPE_SEL" value in it.
;
;       EXIT:   Carry clear, if link ok.
;               Carry set, if link failed.
;
;       USES:   AX, BX, CX, SI, Flags
;

DefProc LinkMap, NEAR, LOCAL

LocVar  MapDefBuff,  <(size MapDef) + MAX_NAME_SIZE>    ; for SegDefLinkUp
LocVar  SegTableBuf, <size SegTable>                    ;
LocVar  ParmAddr,    <size AddrS>                       ;
LocVar  ArgStruc,    <size ArgS>                        ;

BeginProc
        call    SegDefLinkUp
        jc      short lkmx

        mov     si,dataOFFSET addrsFirstMap ; (SI) = pointer to first map AddrS
        lea     bx,SegTableBuf.MapAddrS ; (BX) = pointer to new map AddrS
        mov     cx,(size AddrS)         ; copy AddrS structure
        Pcall   PutBuff                 ; insert New map at head of list.
        jc      short lkme

        Pcall   CopyAddr, <bx, si>
        Pcall   CopyAddr, <si, dataOFFSET addrsLastMap>

        mov     dx,MAP_FLAG_ACTIVE
        Pcall   PutWordOff, <MapFlags, 0> ; initialize map flags
        jc      short lkme

        inc     [cMapCount]             ; one more map

        putstr  "Symbols linked ("      ; print Linked message
        lea     si,MapDefBuff.MapNameLen
        Pcall   PrintPackLC             ; print Map name
        mov     al,")"
        Pcall   putchar
        Pcall   crlf
        clc                             ; clear carry to signal success
lkmx:
EndProc
lkme:   InternalSymErr  <LinkMap>

;***    SegDefLinkUp - Link Groups referenced by Parameter Frame SegDefs.
;
;       call SegDefLinkUp
;
;       NOTE:   This routine cannot use ArgVars or LocVars as it must
;               maintain the same stack frame as it is called with.
;
;       ENTRY:  (BP) = LinkMap/ReLinkMap stack frame
;
;       EXIT:   Carry clear, if no error.
;               Carry set, if error in GetMapDefErr
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc SegDefLinkUp, NEAR, LOCAL
        lea     bx,ParmAddr             ; (BX) = Parameter frame AddrS
        Pcall   FillDefAddr, <bx, RegSA.DSsav, RegSA.ESIsav.LowWord, \
                              RegSA.ESIsav.HighWord>

Entry   SegDefLinkUp1, NEAR, LOCAL
        mov     ArgStruc.ArgAddrPtr,bx  ; Save parameter frame AddrS pointer
        mov     ArgStruc.ArgFlags,0     ; Initialize argument flags
        lea     di,SegTableBuf
        mov     cx,(size SegTable)
        Pcall   GetBuff                 ; Get seg table entry
        jc      short sdue

        lea     bx,SegTableBuf.MapAddrS ; (BX) = Pointer to MapAddrS
        lea     di,MapDefBuff           ; (DI) = Local buffer for MapDef
        Pcall   GetMapDefErr            ; Get local copy of MapDef
        jc      short sdux              ; Exit if error in GetMapDef

;       Get map version number and adjust the abs symbol size
;       (BX) = MapAddrPtr, (DI) = MapDefPtr

        mov     dx,[di].MapEndPtr
        Pcall   ConvertPara2Off         ; (ax:dx) = MapEndPtr offset
        Pcall   GetDwordOff, <dx, ax>   ; (AX) = map version number, (DX) = 0
        .errnz  (EndMapNext   - 0)
        .errnz  (EndMapVerRel - 2)
        jc      short sdue
        or      dx,dx                   ; is the next map pointer 0?
        jnz     short sdue              ;  no, jump to error
        cmp     ax,0500h                ; if v5.00, abs are 32 bits long
        jne     short sdu20

        mov     dx,[di].GroupChain      ; (DX) = segment link to first group
        Pcall   ConvertPara2Off         ; (ax:dx) = GroupChain offset
        add     dx,offset GroupSymSize
        adc     ax,0
        Pcall   GetByteOff, <dx, ax>    ; (AL) = first groupdef's size

        test    al,GRP_SIZE_32          ; is the first groupdef 32 bits?
        jz      short sdu10             ;  no, set them to 32 bits
        xor     dx,dx                   ;  yes, make the count zero
        Pcall   PutWordOff, <offset ConCnt, 0>
        jc      short sdue
        mov     [di].ConCnt,0
        jmp     short sdu20

sdu10:  Pcall   GetByteOff, <offset MapAbsSize, 0>
        jc      short sdue
        or      al,GRP_SIZE_32          ; set abs size to 32 bits
        Pcall   PutByteOff, <offset MapAbsSize, 0>
        jc      short sdue
        or      [di].MapAbsSize,GRP_SIZE_32

;       Go Link SegDefs
;       (BX) = MapAddrPtr, (DI) = MapDefPtr

sdu20:  lea     si,ArgStruc             ; (SI) = Pointer to ArgStruc
        Pcall   EachGroup, <codeOFFSET LinkSegDefs, bx, di, si>
        clc
sdux:
EndProc
sdue:   InternalSymErr  <SegDefLinkUp>

;***    LinkSegDefs - Link Groups that match SegDefs.
;
;       NOTE:   This is an EachGroup Worker Routine.
;
;       This routine is called from EachGroup and is used to
;       link all the groups that having a matching SegDef
;       entry in the parameter frame.
;
;       ENTRY:  (GroupAddrPtr)  = pointer to GroupAddr
;               (GroupDefPtr)   = pointer to GroupDef
;               (ArgPtr)        = pointer to argument structure
;
;       EXIT:   Carry clear. (EachGroup must go through all Groups)
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc LinkSegDefs, NEAR, LOCAL

ArgVar  GroupAddrPtr, WORD
ArgVar  GroupDefPtr,  WORD
ArgVar  ArgPtr,       WORD

BeginProc
        Pcall   EachSegDef, <codeOFFSET LinkGroup, GroupAddrPtr,\
                             GroupDefPtr, ArgPtr>
        clc
EndProc

;***    LinkGroup - Link current Group if it matches current SegDef.
;
;       NOTE:   This is an EachSegDef Worker Routine.
;
;       This routine is called from EachSegDef and is used to
;       compare the current Group to the current SegDef and to
;       link the group if the two are equal.
;
;       ENTRY:  (GroupAddrPtr)  = pointer to GroupAddr
;               (GroupDefPtr)   = pointer to GroupDef
;               (SegDefBuffPtr) = pointer to SegDefBuff
;               (ArgPtr)        = pointer to argument structure
;
;       EXIT:   Carry clear, if Group and SegDef do not match (cont EachSegDef)
;               Carry set, if Group and SegDef do match (halt EachSegDef)
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc LinkGroup, NEAR, LOCAL

ArgVar  GroupAddrPtr,  WORD
ArgVar  GroupDefPtr,   WORD
ArgVar  SegDefBuffPtr, WORD
ArgVar  ArgPtr,        WORD
LocVar  TmpAddr,       <size AddrS>

BeginProc
        mov     si,GroupDefPtr          ; (SI) = Pointer to Current GroupDef
        mov     di,SegDefBuffPtr        ; (DI) = Pointer to Current SegDef
        mov     ax,[si].GroupSegNum     ; (AX) = group's logical segment number
        cmp     ax,[di].SegDefSegNum    ; compare (AX) to SegDef's SegNum
        clc                             ; clear Carry in case of not equal
        jne     short lkgx

;       Copy SegDef Data into GroupDef (Linking Group)

        mov     bx,GroupAddrPtr         ; (BX) = pointer to GroupDef AddrS
        mov     ax,[di].SegDefType      ; (AX) = type, high byte ignored
        Pcall   PutByteOff, <GroupAddrType, 0>
        jc      short lkge
        mov     dx,[di].SegDefSel
        Pcall   PutWordOff, <GroupAddrSel, 0>
        jc      short lkge
        mov     dx,[di].SegDefSeg
        Pcall   PutWordOff, <GroupAddrSeg, 0>
        jc      short lkge
        xor     dx,dx                   ; task number is zero
        Pcall   PutWordOff, <GroupTaskNum, 0>
        jc      short lkge
        mov     cx,[si].SymCnt          ; (CX) = count
        jcxz    short lkge              ; error if no symbols
        dec     cx
        Pcall   GetSymDefValue          ; (AX:DX) = last SymDef value
        Pcall   PutDwordOff, <GroupEndValue, 0>
        jc      short lkge
lkg10:  stc                             ; set carry to halt EachSegDef
lkgx:
EndProc
lkge:   InternalSymErr  <LinkGroup>

;***    ReLinkMap - ReLink a map
;
;       That is, replace all of the GroupDef Selector/Segment
;       values with the new ones in the parameter frame for
;       the map in the parameter frame.  If a segment in the
;       map segment is not in the parameter frame passed, it is
;       removed from the map segment.  This is so we can remove
;       the initialization segments for installable device
;       drivers.
;
;       ENTRY:  Parameter frame address in user's DS:SI (see LinkMap).
;
;       EXIT:   Carry clear, if relink ok.
;               Carry set, if relink failed.
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;

DefProc ReLinkMap, NEAR, LOCAL

LocVar  MapDefBuff,    <(size MapDef) + MAX_NAME_SIZE>  ; For SegDefLinkUp
LocVar  SegTableBuf,   <size SegTable>                  ;
LocVar  ParmAddr,      <size AddrS>                     ;
LocVar  ArgStruc,      <size ArgS>                      ;
LocVar  PrevGroupAddr, <size AddrS>

BeginProc
        call    SegDefLinkUp
        jc      short rlmx

;       Create Previous Group Addr and initialize to GroupChain in MapDef.

        lea     si,SegTableBuf.MapAddrS ; (SI) = Pointer to MapAddr
        lea     bx,PrevGroupAddr        ; (BX) = Pointer to Previous Group Addr
        Pcall   CopyAddr, <si, bx>      ; Copy MapAddr to PrevGroupAddr
        Pcall   AddAddr, <GroupChain, 0>

        lea     di,ArgStruc             ; (DI) = Pointer to ArgStruc
        mov     [di].ArgAddrPtr,si      ; save MapDef AddrS
        mov     [di].ArgAddrPtr2,bx     ; save previous group addr pointer
        lea     bx,MapDefBuff           ; (BX) = Pointer to MapDef buffer

;       Remove links to un-referenced groups
;       (SI) = MapAddrPtr, (BX) = MapDefPtr, (DI) = ArgPtr

        Pcall   EachGroup, <codeOFFSET UnLinkSegDefs, si, bx, di>
        clc
rlmx:
EndProc

;***    UnLinkSegDefs - Remove links to un-referenced groups in parameter frame.
;
;       NOTE:   This is an EachGroup Worker Routine.
;
;       This routine is called from EachGroup and is used to remove links
;       to groups that are not referenced in the new parameter frame.
;       The routine calls EachSegDef to see if the current group matches
;       any of the SegDefs.  If there is no match then the current group
;       is unlinked, otherwise the current group is marked as the previous
;       group and the routine returns to EachGroup to get the next group.
;
;       ENTRY:  (GroupAddrPtr)  = pointer to GroupAddr
;               (GroupDefPtr)   = pointer to GroupDef
;               (ArgPtr)        = pointer to argument structure
;
;       EXIT:   Carry clear. (EachGroup must go through all Groups)
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc UnLinkSegDefs, NEAR, LOCAL

ArgVar  GroupAddrPtr, WORD
ArgVar  GroupDefPtr,  WORD
ArgVar  ArgPtr,       WORD

BeginProc

;       Check each SegDef to see if current group matches any SegDef.

        Pcall   EachSegDef, <codeOFFSET MatchGroup, GroupAddrPtr, GroupDefPtr, ArgPtr>
        mov     di,[ArgPtr]             ; (DI) = Pointer to ArgStuc
        jc      short ulsx              ; Exit if Group matched a SegDef

;       Remove link to Group if no SegDef matched.

        mov     si,[GroupDefPtr]        ; (SI) = Pointer to GroupDef
        mov     bx,[di].ArgAddrPtr2     ; (BX) = Previous Group Addr Ptr
        mov     dx,[si].GroupLink       ; (DX) = Segment Link to next Group

;       Place link to next Group in GroupLink of Previous Group

        Pcall   PutWord
        .errnz  GroupLink
        jc      short ulse

;       Decrement group count in MapDef

        mov     bx,[di].ArgAddrPtr      ; MapDef AddrS
        Pcall   GetWordOff, <GroupCnt, 0>
        jc      short ulse
        dec     dx
        Pcall   PutWordOff, <GroupCnt, 0>
        jc      short ulse

ulsx:   Pcall   CopyAddr, <[GroupAddrPtr], [di].ArgAddrPtr2>
        clc
EndProc
ulse:   InternalSymErr  <UnLinkSegDefs>

;***    MatchGroup - Check if current Group matches current SegDef
;
;       NOTE:   This is an EachSegDef Worker Routine.
;
;       This routine is called from EachSegDef and it is used to check
;       if the current Group matches the current SegDef.  The carry
;       flag is set if there is a match (to halt EachSegDef).
;
;       ENTRY:  (GroupAddr)     = pointer to GroupAddr
;               (GroupDef)      = pointer to GroupDef
;               (SegDefBuff)    = pointer to SegDefBuff
;               (ArgPtr)        = pointer to argument structure
;
;       EXIT:   Carry clear, if SegDef doesn't match GroupDef (cont EachSegDef).
;               Carry set, if SegDef matches GroupDef (halt EachSegDef).
;
;       USES:   AX, SI, Flags

DefProc MatchGroup, NEAR, LOCAL

ArgVar  GroupAddrPtr,  WORD
ArgVar  GroupDefPtr,   WORD
ArgVar  SegDefBuffPtr, WORD
ArgVar  ArgPtr,        WORD

BeginProc
        mov     si,GroupDefPtr          ; (SI) = Pointer to GroupDef
        mov     ax,[si].GroupSegNum     ; (AX) = Logical Group Segment Number
        mov     si,SegDefBuffPtr        ; (SI) = Pointer to SegDef
        cmp     ax,[si].SegDefSegNum    ; Compare AX to Logical SegDef Number
        clc                             ; Clear carry (continue EachSegDef)
        jne     short magx              ; Exit with Carry Clear if equal
        stc                             ; Set carry (halt EachSegDef)
magx:
EndProc

;***    UnLinkMap - UnLink a map from the chain of maps in memory.
;
;       If a device driver fails initialization, then it's gone. So
;       its symbol map is should be "unlinked". This removes it from
;       list of symbol maps maintained by debsym code. The code that calls
;       this routine, (which presumably allocated the memory for the map
;       originally) must also free the memory.
;
;       ENTRY:  Parameter frame address in user's DS:SI (see LinkMap).
;
;       EXIT:   Carry clear, if unlink ok.
;               Carry set, if unlink failed.
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;

DefProc UnLinkMap, NEAR, LOCAL

LocVar  ParmAddr, <size AddrS>
LocVar  ArgStruc, <size ArgS>
LocVar  LinkAddr, <size AddrS>

BeginProc
        lea     bx,ParmAddr             ; (BX) = parameter frame AddrS
        Pcall   FillDefAddr, <bx, RegSA.DSsav, RegSA.ESIsav.LowWord, RegSA.ESIsav.HighWord>
        lea     di,ArgStruc             ; (DI) = pointer to ArgStruc
        mov     [di].ArgAddrPtr,bx      ; save parameter frame AddrS pointer
        mov     [di].ArgFlags,0         ; initialize argument flags

        lea     bx,LinkAddr             ; (BX) = pointer to previous LinkAddr
        mov     [di].ArgAddrPtr2,bx     ; save link AddrS pointer
        Pcall   FillDefAddr, <bx, ss, dataOFFSET addrsFirstMap, 0>

        xor     ah,ah
        mov     al,addrsFirstMap.AddrType
        or      al,al
        jz      short ulm10             ; exit if no link

        Pcall   EachMap, <codeOFFSET RemoveMapLink, 0, 0, di>

ulm10:  Pcall   CopyAddr, <dataOFFSET addrsFirstMap, dataOFFSET addrsLastMap>

;       If (AX) == 0, map not found, set carry to indicate unlink failed
;       If (AX) != 0, map found, clear carry to indicate unlink ok

        cmp     ax,1                    ; carry set if (AX) == 0
EndProc

;***    RemoveMapLink - Removes map from linked list of maps in debugger.
;
;       NOTE:   This is an EachMap Worker Routine.
;
;       This routines is called from EachMap and is used to remove the
;       current map from the linked list of debugger maps if the current
;       map matches the map specified by the user parameter frame.
;
;       ENTRY:  (MapAddrPtr)    = pointer to MapAddr
;               (MapDefPtr)     = pointer to MapDef
;               (ArgPtr)        = pointer to argument structure
;
;       EXIT:   (AX) != 0, map found
;               (AX) == 0, map not found
;               Carry clear, continue EachMap.
;               Carry set, halt EachMap.
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc RemoveMapLink, NEAR, LOCAL

ArgVar  MapAddrPtr, WORD
ArgVar  MapDefPtr,  WORD
ArgVar  ArgPtr,     WORD
LocVar  MapAddr2,   <size AddrS>

BeginProc
        mov     di,ArgPtr               ; (DI) = pointer to argument structure
        mov     bx,[di].ArgAddrPtr      ; (BX) = pointer to ParmFrame AddrS
        lea     di,MapAddr2             ; (DI) = pointer to parm MapAddr buffer
        mov     cx,(size AddrS)         ; copy AddrS structure
        .errnz  MapAddrS
        Pcall   GetBuff                 ; get parm MapAddr
        jc      short rmle

        mov     bx,MapAddrPtr           ; (BX) = MapAddr, (DI) = parm MapAddr
        Pcall   CompareAddr, <bx, di>   ; compare current map to Parm map
        jc      short rmle
        jnz     short rml2              ; advance to next map if not equal

        mov     di,ArgPtr               ; (DI) = pointer to argument structure
        mov     bx,[di].ArgAddrPtr2     ; (BX) = pointer to PrevMapAddr
        mov     si,MapDefPtr            ; (SI) = pointer to CurrMapLink
        lea     si,[si].MapLink.MapAddrS
        mov     cx,(size AddrS)         ; copy AddrS structure
        Pcall   PutBuff                 ; replace PrevMapLink with CurrMapLink
        jc      short rmle

        dec     [cMapCount]             ; one less map

        putstr  "Symbols unlinked ("    ; print unlinked message
        mov     si,MapDefPtr
        lea     si,[si].MapNameLen      ; (SI) = Pointer to name packet
        Pcall   PrintPackLC             ; print Map name
        mov     al,')'
        Pcall   putchar
        Pcall   crlf
        stc                             ; set carry to halt EachMap
        sbb     ax,ax                   ; (AX) != 0, map found, carry set
        jmp     short rmlx              ; exit

;       Copy current map to previous map

rml2:   mov     bx,ArgPtr               ; (BX) = Pointer to argument structure
        Pcall   CopyAddr, <MapAddrPtr, [bx].ArgAddrPtr2>
rml3:   xor     ax,ax                   ; map not found, cont EachMap (CY=0).
rmlx:
EndProc
rmle:   InternalSymErr  <RemoveMapLink>


;***    getsymbuf - search for symbol name from value
;
;       Finds the input address in the map, and copies
;                      the group name and symbol name into the passed buffer.
;
;       err = getsymbuf(Buffer, AddrPtr, SymFlag);
;
;       ENTRY:  (Buffer) = offset of buffer
;               (AddrPtr) = offset of AddrS structure
;               (SymFlag) = flags
;
;       EXIT:   (AX) != 0, if symbol found
;               (AX) == 0, if symbol not found
;
;       USES:   AX, BX, CX, DX, Flags
;
;       Uses C calling convention.

DefProc getsymbuf, NEAR, PUBLIC, C

ArgVar  Buffer,  WORD
ArgVar  AddrPtr, WORD
ArgVar  SymFlag, WORD

BeginProc
        SaveReg <si, di>
        Pcall   GetSymbolBuf, <Buffer, AddrPtr, SymFlag, 0>
        RestReg <di, si>
EndProc

;***    GetSymbolBuf - search for symbol name from value
;
;       Finds the input address in the map, and copies the group
;       name and symbol name into the passed buffer.
;
;       Pcall GetSymbolBuf, <Buffer, AddrPtr, SymFlag, SymValuePtr>
;
;       ENTRY:  (Buffer) = offset of buffer
;               (AddrPtr) = offset of AddrS structure
;               (SymFlag) = flags
;               (SymValuePtr) = AddrS pointer to put actual value found
;
;       EXIT:   (AX) != 0, carry clear, if symbol found
;               (AX) == 0, carry set, if symbol not found
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;

DefProc GetSymbolBuf, NEAR, PUBLIC

ArgVar  Buffer,     WORD
ArgVar  AddrPtr,    WORD
ArgVar  SymFlag,    WORD
ArgVar  SymValuePtr,WORD
LocVar  ArgStruc,   <size ArgS>
LocVar  ActMapDef,  <(size MapDef) + MAX_NAME_SIZE>

BeginProc

        lea     di,ArgStruc             ; (DI) = Pointer to ArgStruc
        mov     ax,[Buffer]             ; (AX) = buffer pointer
        mov     [di].ArgBufPtr,ax
        mov     bx,AddrPtr              ; (BX) = offset of AddrS structure
        mov     [di].ArgAddrPtr,bx

        mov     cx,1
        Pcall   AddrToLin               ; (AX:DX) = linear match value
        jc      short gsb2              ; if address invalid, sym not found
        mov     [di].ArgMatchValue.HighWord,ax
        mov     [di].ArgMatchValue.LowWord,dx

        mov     si,[SymValuePtr]        ; (SI) = found symbol value pointer
        mov     [di].ArgSymValuePtr,si
        or      si,si
        jz      gsb1                    ; if pointer is zero, don't copy
        Pcall   CopyAddr, <bx, si>      ; initialize return symbol value AddrS

gsb1:   mov     ax,[SymFlag]            ; (AX) = get symbol flags
        mov     [di].ArgFlags,al

;       (DI) = pointer to ArgStruc

        Pcall   EachMap, <codeOFFSET EachMapGetSymbolBuf, 0,\
                          (EACHMAP_ACTIVE or EACHMAP_START_LAST), di>
        cmp     ax,1                    ; carry set if (AX) == 0
        jmp     short gsbx

gsb2:   xor     ax,ax
        stc                             ; set carry, (AX) == 0, symbol not found
gsbx:
EndProc

;***    EachMapGetSymbolBuf - GetSymbolBuf's EachMap routine
;
;       NOTE:   This is an EachMap Worker Routine.
;
;       This routine is called from EachMap and to search all maps
;       for a symbol value.
;
;       ENTRY:  (MapAddrPtr)    = pointer to MapAddr
;               (MapDefPtr)     = pointer to MapDef
;               (ArgPtr)        = pointer to argument structure
;
;       EXIT:   (AX) != 0, symbol found
;               (AX) == 0, symbol not found
;               Carry clear, continue EachMap.
;               Carry set, halt EachMap.
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc EachMapGetSymbolBuf, NEAR, LOCAL

ArgVar  MapAddrPtr, WORD
ArgVar  MapDefPtr,  WORD
ArgVar  ArgPtr,     WORD

BeginProc
        mov     di,[ArgPtr]             ; (DI) = argument structure
        mov     bx,[di].ArgAddrPtr      ; (BX) = pointer to address
        mov     cx,codeOFFSET MatchAbsValue
        Pcall   GetAddrType
        .errnz  EXPR_TYPE_DATA
        or      al,al
        je      short emg10             ; jump if data value
        mov     cx,codeOFFSET MatchValueGroup
emg10:  Pcall   EachGroup, <cx, MapAddrPtr, MapDefPtr, di>
EndProc

;***    MatchAbsValue - Match group value with absolute value and return name.
;
;       NOTE:   This is an EachGroup Worker Routine.
;
;       This routine is called from EachGroup and is used to
;       match the search value with the group value and return
;       the group name if a match is found.
;
;       Checks each group value for match against search value
;       Note that each absolute symbol is not checked, this is
;       because there are usually many different symbols with
;       the same value and we won't be sure which is appropriate.
;
;       ENTRY:  (GroupAddrPtr)  = pointer to GroupAddr
;               (GroupDefPtr)   = pointer to GroupDef
;               (ArgPtr)        = pointer to argument structure
;
;       EXIT:   (AX) != 0, symbol found
;               (AX) == 0, symbol not found
;               Carry clear, continue EachSym
;               Carry set, halt EachSym
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc MatchAbsValue, NEAR, LOCAL

ArgVar  GroupAddrPtr, WORD
ArgVar  GroupDefPtr,  WORD
ArgVar  ArgPtr,       WORD
LocVar  addrsTemp,    <size AddrS>

BeginProc
        mov     si,[GroupDefPtr]        ; (SI) = pointer to GroupDef
        lea     bx,addrsTemp            ; (BX) = AddrS to fill in
        Pcall   GetGroupAddr
        Pcall   GetAddrType             ; (AL) = address type
        test    al,EXPR_TYPE_SEGSEL
        jz      short mab10             ; jump if not selector/segment

        mov     dx,[bx].AddrSeg         ; (DX) = group selector/segment
        mov     si,[ArgPtr]             ; (SI) = pointer to argument structure
        mov     bx,[si].ArgAddrPtr      ; (BX) = pointer to AddrS
        cmp     dx,[bx].AddrOff.LowWord ; compare search value to group value
        jne     short mab10             ; exit, (AX) == 0, symbol not found
        cmp     [bx].AddrOff.HighWord,0
        jne     short mab10             ; exit, (AX) == 0, symbol not found

        mov     bx,[GroupDefPtr]        ; (BX) = pointer to GroupDef
        mov     di,[si].ArgBufPtr       ; (DI) = pointer to name buffer
        lea     si,[bx].GroupNameLen    ; (SI) = pointer to group name packet
        Pcall   CopySymbol              ; copy group name to buffer
        stc                             ; set carry to halt EachGroup
        sbb     ax,ax                   ; (AX) != 0, symbol found, carry set
        jmp     mabx

mab10:  xor     ax,ax                   ; (AX) == 0, sym not found, carry clear
mabx:
EndProc

;***    MatchValueGroup - matches value to group
;
;       NOTE:   This is an EachGroup Worker Routine.
;
;       This routine is called from EachGroup and is used to
;       compare the current group to the search group and to
;       search the group symbols if a match is found.
;
;       ENTRY:  (paddrsGroupDef) = pointer to GroupAddr
;               (pGroupDef)      = pointer to GroupDef
;               (pargs)          = pointer to argument structure
;
;       EXIT:   (AX) != 0, symbol found
;               (AX) == 0, symbol not found
;               Carry clear, continue EachGroup.
;               Carry set, halt EachGroup.
;
;       NOTE:   GroupDef.SymCnt must not be zero.
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc MatchValueGroup, NEAR, LOCAL

ArgVar  mvg_paddrsGroupDef,WORD
ArgVar  mvg_pGroupDef,WORD
ArgVar  mvg_pargs,WORD
LocVar  mvg_addrsGroup,<size AddrS>
LocVar  mvg_ulSearchValue,DWORD

BeginProc
        mov     si,[mvg_pGroupDef]      ; (SI) = GroupDef pointer
        lea     bx,[mvg_addrsGroup]     ; (BX) = group AddrS
        Pcall   GetGroupAddr
        mov     cx,1
        Pcall   AddrToLin               ; (AX:DX) = base value
        jc      short mvg20             ;  if error, skip group

        mov     di,[mvg_pargs]          ; (DI) = Arg structure pointer
        mov     cx,[di].ArgMatchValue.LowWord ; (BX:CX) = match value
        mov     bx,[di].ArgMatchValue.HighWord

        cmp     bx,ax
        jb      short mvg20             ; if match value < base value, exit
        ja      short mvg10
        cmp     cx,dx
        jb      short mvg20             ; if match value < base value, exit

mvg10:  sub     cx,dx
        sbb     bx,ax                   ; (BX:CX) = match value - base value
        mov     [mvg_ulSearchValue].LowWord,cx
        mov     [mvg_ulSearchValue].HighWord,bx

        cmp     bx,[si].GroupEndValue.HighWord
        ja      short mvg80             ; if match value > end value, exit
        jb      short mvg30
        cmp     cx,[si].GroupEndValue.LowWord
        ja      short mvg80             ; if match value > end value, exit
        jne     short mvg30             ; jump if not exact match

        mov     cx,[si].SymCnt          ; (CX) = number of symbols
        dec     cx                      ; (CX) = end symbol index
        jmp     short mvg60             ; exact match found, go copy name

mvg20:  jmp     short mvg80             ; jump to exit

mvg30:  Pcall   SearchValue, <cx, bx, [mvg_paddrsGroupDef], si>
        or      ax,ax
        jnz     short mvg60             ; jmp if match found, (CX) = index

;       (DI) = previous symbol index
;       (SI) = next symbol index

        mov     bx,[mvg_pargs]          ; (BX) = ArgS pointer
        test    [bx].ArgFlags,MAP_NEAR_PREV
        jz      short mvg40             ; jump to next symbol check
        or      di,di
        jl      short mvg70             ; if DI < 0, symbol not found
        mov     cx,di                   ; (CX) = previous symbol index
        jmp     short mvg50

mvg40:  test    [bx].ArgFlags,MAP_NEAR_NEXT
        jz      short mvg70             ; jump to symbol not found
        mov     di,[mvg_pGroupDef]      ; (DI) = GroupDef pointer
        cmp     si,[di].SymCnt
        jae     short mvg70             ; if CX >= SymCnt, symbol not found
        mov     cx,si                   ; (CX) = next symbol index

mvg50:  cmp     [bx].ArgSymValuePtr,0   ; is there a sym value pointer
        je      short mvg60             ; don't do any if null

        mov     bx,[mvg_paddrsGroupDef] ; (BX) = GroupDef AddrS pointer
        mov     si,[mvg_pGroupDef]      ; (SI) = GroupDef pointer
        Pcall   GetSymDefValue          ; (AX:DX) = symbol value
        sub     dx,[mvg_ulSearchValue].LowWord
        sbb     ax,[mvg_ulSearchValue].HighWord ; (AX:DX) = linear value

        mov     di,[mvg_pargs]          ; (DI) = ArgS pointer
        mov     di,[di].ArgSymValuePtr  ; (DI) = pointer to symbol value addrS
        add     [di].AddrOff.LowWord,dx
        adc     [di].AddrOff.HighWord,ax

mvg60:  mov     bx,[mvg_paddrsGroupDef] ; (BX) = GroupDef AddrS pointer
        mov     si,[mvg_pGroupDef]      ; (SI) = GroupDef pointer
        mov     di,[mvg_pargs]          ; (DI) = ArgS pointer
        Pcall   GetSymDefName
        stc                             ; set carry to halt EachGroup
        sbb     ax,ax                   ; (AX) != 0, symbol found, carry set
        jmp     short mvgx

mvg70:  xor     ax,ax                   ; (AX) == 0, symbols not found
        stc                             ; set carry to halt EachGroup
        jmp     short mvgx

mvg80:  xor     ax,ax                   ; (AX) == 0, symbol not found
mvgx:
EndProc

;***    SearchValue - binary search the symdefs for value
;
;       ENTRY:  (ulMatchValue)
;                       value to find symbol for
;               (paddrsGroupDef)
;                       pointer to GroupDef AddrS
;               (pGroupDef)
;                       pointer to GroupDef
;
;       EXIT:   (AX) != 0, symbol found
;                       (CX) = index to matching SymDef
;               (AX) == 0, symbol not found
;                       (DI) = index to previous SymDef (-1 if none)
;                       (SI) = index to next SymDef
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;
;       NOTE:   GroupDef.SymCnt must not be zero.
;
;       PSEUDO-CODE:
;
;       lower = 0
;       upper = cSymbol - 1
;       while(lower <= upper) {
;           middle = (upper + lower)/2
;           SymbolValue = GetSymDefValue(middle, paddrsGroupDef, pGroupDef)
;           if(MatchValue == SymbolValue) {
;               return(middle, symbol found)
;           }
;           else if(MatchValue > SymbolValue) { // upper half
;               lower = middle + 1
;           }
;           else {                              // lower half
;               upper = middle - 1
;           }
;       }
;       return(middle, symbol not found)
;

DefProc SearchValue, NEAR, LOCAL

ArgVar  sdv_ulMatchValue,DWORD
ArgVar  sdv_paddrsGroupDef,WORD
ArgVar  sdv_pGroupDef,WORD

BeginProc
        mov     di,[sdv_pGroupDef]      ; (DI) = GroupDef pointer
        mov     di,[di].SymCnt
        dec     di                      ; (DI) = upper bound
        xor     si,si                   ; (SI) = lower bound
        xor     cx,cx                   ; (CX) = middle
        mov     bx,[sdv_paddrsGroupDef] ; (BX) = paddrGroupDef

;       (SI) = lower bound
;       (DI) = upper bound
;       (CX) = middle

sdv10:  cmp     si,di                   ; if lower > upper
        jg      short sdv40             ;  yes, jump to exit

        mov     cx,di                   ; (CX) = upper bound
        add     cx,si                   ; (CX) = upper + lower
        shr     cx,1                    ; (CX) = (upper + lower)/2

        push    si
        mov     si,[sdv_pGroupDef]
        Pcall   GetSymDefValue          ; (AX:DX) = symbol value
        pop     si

;       if match value == symbol value, we found the symbol
;       if match value > symbol value, symbol in upper half
;       if match value < symbol value, symbol in lower half

        cmp     [sdv_ulMatchValue.HighWord],ax
        ja      short sdv20             ; jump if > symbol
        jb      short sdv30             ; jump if < symbol
        cmp     [sdv_ulMatchValue.LowWord],dx
        ja      short sdv20             ; jump if > symbol
        jb      short sdv30             ; jump if < symbol

;       Symbol matched exactly, [sdv_offSymDef] contains SymDef offset

        mov     ax,1                    ; (AX) = symbol found
        jmp     short sdvx              ; jump to exit

;       Symbol in upper half

sdv20:  mov     si,cx
        inc     si                      ; (SI) = middle + 1
        jmp     short sdv10

;       Symbol in lower half

sdv30:  mov     di,cx
        dec     di                      ; (DI) = middle - 1
        jns     short sdv10             ; jump if di != -1
sdv40:  xor     ax,ax                   ; (AX) == 0, symbol not found
sdvx:
EndProc

;***    GetSymDefValue - returns the SymDef's value
;
;       ENTRY:  (BX) = paddrsGroupDef
;               (SI) = pGroupDef
;               (CX) = SymDef's SymChain index
;
;       EXIT:   (AX:DX) = symbol value
;
;       USES:   AX, DX, Flags
;

DefProc GetSymDefValue, NEAR, LOCAL
.386p                                                                    ;p46737
        push    ecx                                                      ;p46737
        push    eax                                                      ;p46737
        movzx   eax,[si].SymChain                                        ;p46737
        movzx   ecx,cx                                                   ;p46737
        add     ecx,ecx                 ; (ECX) = word index             ;p46737
        add     ecx,eax                 ; (ECX) = SymChain array offset  ;p46737
        pop     eax                                                      ;p46737
        Pcall   GetWordOff, <ecx>       ; (DX)  = SymDef offset          ;p46737
        pop     ecx                                                      ;p46737
.286p                                                                    ;p46737
        jc      short gdve

        test    [si].GroupSymSize,GRP_SIZE_32
        jnz     short gdv10

        Pcall   GetWordOff, <dx, 0>     ; (DX) = symbol value
        .errnz  SymValue
        jc      short gdve
        xor     ax,ax
        jmp     short gdvx

gdv10:  Pcall   GetDwordOff, <dx, 0>    ; (AX:DX) = symbol value
        .errnz  SymValue32
        jc      short gdve
gdvx:
EndProc
gdve:   InternalSymErr  <GetSymDefValue>

;***    GetSymDefName - copies the symbol name to buffer
;
;       ENTRY:  (BX) = GroupDef AddrS
;               (CX) = SymDef's SymChain index
;               (SI) = pGroupDef
;               (DI) = ArgPtr
;
;       EXIT:   (DI) = buffer pointer after symbol
;
;       USES:   AX, CX, DX, SI, DI, Flags
;

DefProc GetSymDefName, NEAR, LOCAL
.386p                                                                    ;p46737
        push    ecx                                                      ;p46737
        push    eax                                                      ;p46737
        movzx   eax,[si].SymChain                                        ;p46737
        movzx   ecx,cx                                                   ;p46737
        add     ecx,ecx                 ; (ECX) = word index             ;p46737
        add     ecx,eax                 ; (ECX) = SymChain array offset  ;p46737
        pop     eax                                                      ;p46737
        Pcall   GetWordOff, <ecx>       ; (DX)  = SymDef offset          ;p46737
        pop     ecx                                                      ;p46737
.286p                                                                    ;p46737
        jc      short gdne

        mov     al,[di].ArgFlags        ; (AX) = arg flags
        mov     di,[di].ArgBufPtr       ; (DI) = pointer to name buffer

        test    al,MAP_GETMAPNAME
        jz      short gdn00             ; No group name if flag not set
        push    ax
        push    si
        mov     si,[pMapDefCur]         ; (SI) = pointer to current Map
        add     si,offset MapNameLen    ; (SI) = Pointer to name packet
        push    di
        Pcall   CopySymbol              ; copy group name to buffer
        pop     si                      ; (SI) = start, (DI) = end
        Ccall   lowercase,<si>          ; lower case the map name
        mov     byte ptr [di],':'       ; insert colon between map and group
        inc     di
        pop     si
        pop     ax

gdn00:  test    al,MAP_GETGROUP
        jz      short gdn10             ; No group name if flag not set

        push    si
        add     si,offset GroupNameLen  ; (SI) = pointer to group name packet
        Pcall   CopySymbol              ; copy group name to buffer
        mov     byte ptr [di],':'       ; insert colon between group and symbol
        inc     di
        pop     si

;       (BX) = base AddrS
;       (DX) = offset to SymDef
;       (SI) = GroupDef pointer
;       (DI) = buffer pointer

gdn10:  add     dx,offset SymNameLen
        test    [si].GroupSymSize,GRP_SIZE_32
        jz      short gdn20             ; jump if 16 bit SymDef
        add     dx,offset (SymNameLen32 - SymNameLen)

;       (BX) = base AddrS
;       (DX) = offset to SymDef name field
;       (DI) = buffer pointer

gdn20:  push    dx
        Pcall   GetByteOff, <dx, 0>     ; (AL) = symbol name length
        jc      short gdne
        xor     ch,ch
        mov     cl,al                   ; (CX) = symbol name length
        pop     dx
        inc     dx                      ; (DX) = offset ot symbol name
        .errnz  (SymName - SymNameLen) - 1
        .errnz  (SymName32 - SymNameLen32) - 1

        cmp     cx,MAX_NAME_SIZE - 1    ; check size, leave space for NULL
        jbe     short gdn30
        mov     cx,MAX_NAME_SIZE - 1

gdn30:  Pcall   GetBuffOff, <dx, 0>
        jc      short gdne
        add     di,cx                   ; (DI) = points to end of symbol name
        xor     al,al
        stosb                           ; zero terminate symbol
EndProc
gdne:   InternalSymErr  <GetSymDefName>

;***    GetSymbolVal - Searches the active symbol map for a symbol.
;
;       Scans the active symbol map for the input address-type
;       or constant-type symbol, and returns its value if found.
;
;       ENTRY:  (DI) =  points to "name packet".
;               (SI) =  pointer to Addrs/ExprS to put value in
;
;       EXIT:   Carry clear, symbol found.
;               Carry set, symbol not found.
;
;       Name Packet:
;
;       The name packet must be of the following form.  A byte indicating
;       the length of the string, followed by the ascii string that represents
;       the symbol.
;
;       USES:   AX, BX, CX, DX, Flags
;

DefProc GetSymbolVal, NEAR, PUBLIC

LocVar  ArgStruc,   <size ArgS>

BeginProc
        SaveReg <si, di>
        lea     bx,ArgStruc             ; (BX) = Pointer to ArgStruc
        mov     [bx].ArgBufPtr,di       ; save pointer to search name
        mov     [bx].ArgAddrPtr,si      ; save pointer to AddrS
        mov     [bx].ArgFlags,0         ; initialize argument flags
        Pcall   EachMap, <codeOFFSET EachMapGetSymbolValue, 0,\
                          (EACHMAP_ACTIVE or EACHMAP_START_LAST), bx>
        cmp     ax,1                    ; exit with carry set if not found
        RestReg <di, si>
EndProc

;***    EachMapGetSymbolValue - GetSymbolValue's EachMap routine
;
;       NOTE:   This is an EachMap Worker Routine
;
;       This routine is called from EachMap and to search all maps
;       for a symbol value.
;
;       ENTRY:  (MapAddrPtr)    = pointer to MapAddr
;               (MapDefPtr)     = pointer to MapDef
;               (ArgPtr)        = pointer to argument structure
;
;       EXIT:   (AX) != 0, symbol found
;               (AX) == 0, symbol not found
;               Carry clear, continue EachMap.
;               Carry set, halt EachMap.
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc EachMapGetSymbolValue, NEAR, LOCAL

ArgVar  MapAddrPtr, WORD
ArgVar  MapDefPtr,  WORD
ArgVar  ArgPtr,     WORD

BeginProc
        Pcall   EachGroup, <codeOFFSET MatchGroupSymName, \
                            [MapAddrPtr], [MapDefPtr], [ArgPtr]>
        or      ax,ax                   ; if (AX) != 0, symbol found
        jnz     short gsv30             ; exit if symbol found, carry cleared

        mov     di,[MapDefPtr]          ; (DI) = map def pointer
        test    [di].MapAbsSize,GRP_SIZE_32
        jnz     short gsv10
        Pcall   EachAbsSym, <codeOFFSET MatchAbsSymName, \
                             [MapAddrPtr], [MapDefPtr], [ArgPtr]>
        jmp     short gsv20

gsv10:  Pcall   EachAbsSym32, <codeOFFSET MatchAbsSymName32, \
                             [MapAddrPtr], [MapDefPtr], [ArgPtr]>
gsv20:  cmp     ax,1                    ; exit with carry set if not found
gsv30:  cmc
EndProc

;***    MatchGroupSymName - Match search symbol name with Group or Symbol name
;
;       NOTE:   This is an EachGroup Worker Routine.
;
;       This routine is called from EachGroup and is used to
;       compare the search symbol name with each group name
;       and each symbol name until a match is found.  If a
;       match is found then the symbol value is returned.
;
;       ENTRY:  (GroupAddrPtr)  = pointer to GroupAddr
;               (GroupDefPtr)   = pointer to GroupDef
;               (ArgPtr)        = pointer to argument structure
;
;       EXIT:   (AX) != 0, symbol found
;               (AX) == 0, symbol not found
;               Carry clear, continue EachGroup
;               Carry set, halt EachGroup
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc MatchGroupSymName, NEAR, LOCAL

ArgVar  GroupAddrPtr, WORD
ArgVar  GroupDefPtr,  WORD
ArgVar  ArgPtr,       WORD

BeginProc
        mov     bx,ArgPtr               ; (BX) = pointer to arguments
        mov     si,[bx].ArgBufPtr       ; (SI) = pointer to search symbol name
        mov     di,GroupDefPtr          ; (DI) = pointer to GroupDef buffer
        lea     di,[di].GroupNameLen    ; (DI) = pointer to group name packet
        Pcall   CompPacket              ; compare name packets
        jc      short msn1              ; check symbols if group doesn't match
        mov     si,GroupDefPtr          ; (SI) = Pointer to GroupDef Buffer
        mov     bx,[bx].ArgAddrPtr      ; (BX) = AddrS
        Pcall   GetGroupAddr
        stc                             ; set carry to stop EachGroup
        sbb     ax,ax                   ; (AX) != 0, symbol found, carry set
        jmp     short msnx              ; exit

;       Check each symbol for match with search symbol and return value
;       if found.  If it is a 32bit group, use EachSym32 to scan the symbol
;       list.

msn1:   mov     di,GroupDefPtr          ; (DI) = ptr to group def
        test    [di].GroupSymSize,GRP_SIZE_32
        jnz     short msn2              ; it is a 32-bit group
        Pcall   EachSym, <codeOFFSET MatchSymName, GroupAddrPtr, di, ArgPtr>
        jmp     short msnx              ; exit

msn2:   Pcall   EachSym32, <codeOFFSET MatchSymName32, GroupAddrPtr, di, ArgPtr>
msnx:
EndProc

;***    MatchSymName - Match symbol name and get value.
;
;       NOTE:   This is an EachSym Worker Routine.
;
;       This routine is called from EachSym and is used to
;       compare the search symbol name with the current symbol
;       name and return the symbol value if a match is found.
;
;       ENTRY:  (ArgPtr)      = pointer to argument structure
;               (GroupDefPtr) = pointer to GroupDef
;               (SymDefPtr)   = pointer to SymDef
;
;       EXIT:   (AX) != 0, symbol found
;               (AX) == 0, symbol not found
;               Carry clear, continue EachSym
;               Carry set, halt EachSym
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc MatchSymName, NEAR, LOCAL

ArgVar  ArgPtr,      WORD
ArgVar  GroupDefPtr, WORD
ArgVar  SymDefPtr,   WORD

BeginProc
        xor     ax,ax                   ; (AX) == 0, symbol not found
        mov     si,[ArgPtr]             ; (SI) = Pointer to arguments
        mov     si,[si].ArgBufPtr       ; (SI) = Pointer to search symbol name
        mov     di,[SymDefPtr]          ; (DI) = Pointer to SymDef
        lea     di,[di].SymNameLen      ; (DI) = Pointer to current symbol name
        Pcall   CompPacket              ; compare symbol name packets
        jc      short mgv10             ; exit if not equal, continue EachSym
        mov     bx,[ArgPtr]             ; (BX) = Pointer to arguments
        mov     bx,[bx].ArgAddrPtr      ; (BX) = AddrS to fill in
        mov     si,[GroupDefPtr]        ; (DI) = Pointer to GroupDef Buffer
        Pcall   GetGroupAddr
        mov     si,[SymDefPtr]
        Pcall   AddAddr, <[si].SymValue, 0>
        xor     ax,ax                   ; carry set (after cmc) to halt EachSym
        not     ax                      ; (AX) != 0, symbol found
mgv10:  cmc                             ; complement carry for right sense
EndProc

;***    MatchSymName32 - Match symbol name and get value.
;
;       NOTE:   This is an EachSym32 Worker Routine.
;
;       This routine is called from EachSym32 and is used to
;       compare the search symbol name with the current symbol
;       name and return the symbol value if a match is found.
;
;       ENTRY:  (ArgPtr)      = pointer to argument structure
;               (GroupDefPtr) = pointer to GroupDef
;               (SymDefPtr)   = pointer to SymDef32
;
;       EXIT:   (AX) != 0, symbol found
;               (AX) == 0, symbol not found
;               Carry clear, continue EachSym32
;               Carry set, halt EachSym32
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc MatchSymName32, NEAR, LOCAL

ArgVar  ArgPtr,      WORD
ArgVar  GroupDefPtr, WORD
ArgVar  SymDefPtr,   WORD

BeginProc
        xor     ax,ax                   ; (AX) == 0, symbol not found
        mov     si,[ArgPtr]             ; (SI) = Pointer to arguments
        mov     si,[si].ArgBufPtr       ; (SI) = Pointer to search symbol name
        mov     di,[SymDefPtr]          ; (DI) = Pointer to SymDef
        lea     di,[di].SymNameLen32    ; (DI) = Pointer to current symbol name
        Pcall   CompPacket              ; compare symbol name packets
        jc      short mnm1              ; exit if not equal, continue EachSym
        mov     bx,[ArgPtr]             ; (BX) = Pointer to arguments
        mov     bx,[bx].ArgAddrPtr      ; (BX) = AddrS to fill in
        mov     si,[GroupDefPtr]        ; (DI) = Pointer to GroupDef Buffer
        Pcall   GetGroupAddr
        mov     si,[SymDefPtr]
        Pcall   AddAddr, <[si].SymValue32.LowWord, [si].SymValue32.HighWord>
        xor     ax,ax                   ; carry set (after cmc) to halt EachSym
        not     ax                      ; (AX) != 0, symbol found
mnm1:   cmc                             ; complement carry for right sense
EndProc

;***    MatchAbsSymName - Match absolute symbol name and get value.
;
;       NOTE:   This is an EachAbsSym Worker Routine.
;
;       This routine is called from EachAbsSym and is used to
;       compare the search symbol name with the current symbol
;       name and return the symbol value if a match is found.
;
;       ENTRY:  (ArgPtr)      = pointer to argument structure
;               (MapDefPtr)   = pointer to MapDefPtr
;               (SymDefPtr)   = pointer to SymDef
;
;       EXIT:   (AX) != 0, symbol found
;               (AX) == 0, symbol not found
;               Carry clear, continue EachAbsSym
;               Carry set, halt EachAbsSym
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc MatchAbsSymName, NEAR, LOCAL

ArgVar  ArgPtr,      WORD
ArgVar  MapDefPtr,   WORD
ArgVar  SymDefPtr,   WORD

BeginProc
        xor     ax,ax                   ; (AX) == 0, symbol not found
        mov     si,[ArgPtr]             ; (SI) = Pointer to arguments
        mov     si,[si].ArgBufPtr       ; (SI) = Pointer to search symbol name
        mov     di,[SymDefPtr]          ; (DI) = Pointer to SymDef
        lea     di,[di].SymNameLen      ; (DI) = Pointer to current symbol name
        Pcall   CompPacket              ; compare symbol name packets
        jc      short abn1              ; exit if not equal, continue EachSym
        mov     bx,[ArgPtr]             ; (BX) = Pointer to arguments
        mov     bx,[bx].ArgAddrPtr      ; (BX) = AddrS to fill in
        mov     si,[SymDefPtr]
        Pcall   FillDataAddr, <bx, [si].SymValue, 0>
        mov     ax,1                    ; (AX) != 0, symbol found
        clc                             ; carry set (after cmc) to halt EachSym
abn1:   cmc                             ; complement carry for right sense
EndProc

;***    MatchAbsSymName32 - Match absolute symbol name and get value.
;
;       NOTE:   This is an EachAbsSym32 Worker Routine.
;
;       This routine is called from EachAbsSym32 and is used to
;       compare the search symbol name with the current symbol
;       name and return the symbol value if a match is found.
;
;       ENTRY:  (ArgPtr)      = pointer to argument structure
;               (MapDefPtr)   = pointer to MapDefPtr
;               (SymDefPtr)   = pointer to SymDef32
;
;       EXIT:   (AX) != 0, symbol found
;               (AX) == 0, symbol not found
;               Carry clear, continue EachAbsSym32
;               Carry set, halt EachAbsSym32
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc MatchAbsSymName32, NEAR, LOCAL

ArgVar  ArgPtr,      WORD
ArgVar  MapDefPtr,   WORD
ArgVar  SymDefPtr,   WORD

BeginProc
        xor     ax,ax                   ; (AX) == 0, symbol not found
        mov     si,[ArgPtr]             ; (SI) = Pointer to arguments
        mov     si,[si].ArgBufPtr       ; (SI) = Pointer to search symbol name
        mov     di,[SymDefPtr]          ; (DI) = Pointer to SymDef
        lea     di,[di].SymNameLen32    ; (DI) = Pointer to current symbol name
        Pcall   CompPacket              ; compare symbol name packets
        jc      short man1              ; exit if not equal, continue EachSym
        mov     bx,[ArgPtr]             ; (BX) = Pointer to arguments
        mov     bx,[bx].ArgAddrPtr      ; (BX) = AddrS to fill in
        mov     si,[SymDefPtr]
        Pcall   FillDataAddr, <bx, [si].SymValue32.LowWord, \
                               [si].SymValue32.HighWord>
        mov     ax,1                    ; (AX) != 0, symbol found
        clc                             ; carry set (after cmc) to halt EachSym
man1:   cmc                             ; complement carry for right sense
EndProc

;***    WithMap - Activate the specified symbol map.
;
;       "WA <mapname> | *" or "WR <mapname> | *" command.
;
;       ENTRY:  NONE
;
;       EXIT:   Flags
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc WithMap, NEAR, PUBLIC
        Pcall   PeekChr
        jz      short wmp10
        xor     di,di                   ; (DI) = ActivateMap flag (REMOVE)
        cmp     al,'R'                  ; remove active map
        je      short wmp20
        inc     di                      ; (DI) = ActivateMap flag (ADD)
        cmp     al,'A'                  ; add active map
        je      short wmp20
wmp10:  mov     dx,dataOFFSET WithMapMsg
        Pcall   rprbuf
        jmp     short wmpx

wmp20:  Pcall   SkipB
        Pcall   GetChr                  ; (AL) = next character after spaces
        jz      short wmp50
        mov     si,dataOFFSET AtomBuf
        cmp     al,'*'
        jne     short wmp30
        Pcall   SkipB
        xor     si,si                   ; all maps for "*"
        jmp     short wmp40

wmp30:  Pcall   ScanAtom                ; get search map name
wmp40:  Pcall   EachMap, <codeOFFSET ActivateMap, si, 0, di>
        or      ax,ax
        jnz     short wmpx              ; jump if map found
wmp50:  putstr  <"Invalid map name", CR, LF>
wmpx:
EndProc

;***    ActivateMap - Activate current Map if it matches search Map
;
;       NOTE:   This is an EachMap Worker Routine.
;
;       This routine is called from EachMap and is used to compare
;       the search map name to the current map and activate the
;       current map if a match exists.
;
;       ENTRY:  (MapAddrPtr)    = pointer to MapAddr
;               (MapDefPtr)     = pointer to MapDef
;               (ArgPtr)        = pointer to argument structure
;
;       EXIT:   (AX) != 0, map found
;               (AX) == 0, map not found
;               Carry clear, continue EachMap.
;               Carry set, halt EachMap.
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc ActivateMap, NEAR, LOCAL

ArgVar  MapAddrPtr, WORD
ArgVar  MapDefPtr,  WORD
ArgVar  ArgPtr,     WORD

BeginProc
        mov     bx,[MapAddrPtr]
        Pcall   GetWordOff, <MapFlags, 0>
        jc      short acme
        cmp     [ArgPtr],0              ; if ArgPtr == 0, remove active map
        je      short acm10
        or      dx,MAP_FLAG_ACTIVE      ; if ArgPtr != 0, add active map
        jmp     short acm20

acm10:  and     dx,not MAP_FLAG_ACTIVE
acm20:  Pcall   PutWordOff, <MapFlags, 0>
        jc      short acme
        xor     ax,ax                   ; clears carry, continue EachMap
        not     ax                      ; (AX) != 0, found symbol
EndProc
acme:   InternalSymErr  <ActivateMap>

;***    Lists - List (maps, groups, symbols, near symbols, absolute symbols.)
;
;       "Lists" will print on the debugger terminal the names of the
;       groups, symbols, maps, or near symbols currently active.
;
;       ENTRY:  No explicit arguments.
;
;       EXIT:   NONE
;
;       USES:   Flags
;
;       CALLS:  Either: ListMaps,ListGroups,ListSyms,ListNear,ListAbsSyms
;

DefProc Lists, NEAR, PUBLIC
        Pcall   GetChrI                 ; (AL) = subcommand character
        jz      short lste
        mov     si,dataOFFSET ListTable ; get ptr to function table
        Pcall   ExecSubr                ; call subroutine from table.
EndProc
lste:   Jump    perr

;***    ListNear - Lists symbols that preceeds and follow input address.
;
;       "LN [<addr>]" command.  The command buffer may contain an address
;       or symbol (if not, use default) which is an anchor.  This routine
;       will find and print on the debugging terminal, the symbol whose
;       address preceeds the anchor, and the first symbol whose address
;       is equal to or greater than the anchor.  In addition the symbols'
;       offsets and relative offsets (to the anchor) are printed.
;
;       ENTRY:  Command buffer contains specified address.
;
;       EXIT:   NONE
;
;       USES:   Flags, AX, BX, CX, DX, SI, DI, Flags

DefProc ListNear, NEAR, LOCAL

LocVar  lns_SymBuff,      <3*MAX_NAME_SIZE>
LocVar  lns_NearAddr,     <size AddrS>
LocVar  lns_FoundSymAddr, <size AddrS>

BeginProc
        lea     bx,[lns_NearAddr]       ; (BX) = AddrS pointer
        Pcall   EvalAddr, <dataOFFSET DisAddr, bx>

        lea     si,[lns_SymBuff]        ; (SI) = symbol name buffer
        lea     di,[lns_FoundSymAddr]   ; (DI) = address of symbol found
        Pcall   GetSymbolBuf, <si, bx, <MAP_GETMAPNAME+MAP_GETGROUP+MAP_NEAR_PREV>, di>
        mov     ax,MAP_GETMAPNAME+MAP_GETGROUP+MAP_NEAR_NEXT
        jc      short lns50             ; jump if sym not found, (AX) == flags

        lea     bx,[lns_FoundSymAddr]
        Pcall   OutAddrS, <bx>          ; print the address found
        Pcall   blank

        lea     dx,[lns_SymBuff]
        Pcall   rprbuf                  ; print the previous symbol

        lea     si,[lns_NearAddr]       ; (SI) = AddrS pointer
        lea     di,[lns_FoundSymAddr]   ; (DI) = address of symbol found
        Pcall   SubtractAddr, <si, di>  ; (AX:DX) = NearAddr - FoundSymAddr
        jc      short lns90             ; jump if error

        push    ax
        or      ax,dx
        pop     ax
        jz      short lns90             ; exit if exact match

        putstr  " + "
        Pcall   OutHex                  ; print difference
        Pcall   crlf

        mov     ax,MAP_NEAR_NEXT
lns50:  lea     si,[lns_SymBuff]        ; (SI) = symbol name buffer
        lea     bx,[lns_NearAddr]       ; (BX) = AddrS pointer
        lea     di,[lns_FoundSymAddr]   ; (DI) = address of symbol found
        push    ax
        Pcall   GetSymbolBuf, <si, bx, ax, di>
        pop     ax
        jc      short lns80

        lea     bx,[lns_FoundSymAddr]
        Pcall   OutAddrS, <bx>          ; print the address found
        Pcall   blank

        lea     dx,[lns_SymBuff]        ; print the next symbol
        Pcall   rprbuf

        lea     si,[lns_NearAddr]       ; (SI) = AddrS pointer
        lea     di,[lns_FoundSymAddr]   ; (DI) = address of symbol found
        Pcall   SubtractAddr, <di, si>  ; (AX:DX) = FoundSymAddr - NearAddr
        jc      short lns90             ; jump if error

        push    ax
        or      ax,dx
        pop     ax
        jz      short lns90             ; exit if exact match

        putstr  " - "
        Pcall   OutHex                  ; print difference
        jmp     short lns90             ; Exit

lns80:  test    ax,not MAP_NEAR_NEXT
        jz      short lnsx              ; jump if previous symbol was printed
        putstr  <"No symbols found">
lns90:  Pcall   crlf
lnsx:
EndProc

;***    PrintNearSym - Prints symbol near the address
;
;       ENTRY:  (SI) = Pointer to AddrS
;               (CX) = Flags
;
;       EXIT:   If carry clear, symbol printed
;               if carry set, symbol not found
;
;       USES:   AX, BX, DX, Flags
;

DefProc PrintNearSym, NEAR, PUBLIC

LocVar  SymBuff,      <(MAX_NAME_SIZE*2)+1>
LocVar  FoundSymAddr, <size AddrS>

BeginProc
        SaveReg <si, di>
        lea     bx,SymBuff              ; (BX) = symbol name buffer
        lea     di,FoundSymAddr         ; (DI) = address of symbol found
        SaveReg <si, cx>
        or      cx,MAP_NEAR_PREV        ; (CX) = flags for GetSymbolBuf
        Pcall   GetSymbolBuf, <bx, si, cx, di>
        RestReg <cx, si>
        jc      short pnsx

;       (CX) = symbol flags

        test    cx,MAP_BRACKET
        jz      short pns1
        mov     al,'['                  ; print brackets around the symbol
        Pcall   putchar

pns1:   lea     dx,SymBuff              ; print the next symbol
        Pcall   rprbuf

;       (SI) = search AddrS pointer

        lea     di,FoundSymAddr         ; (DI) = address of symbol found
        Pcall   SubtractAddr, <si, di>  ; (AX:DX) = search addr - FoundSymAddr
        jc      short pns2              ; jump if error

        push    ax
        or      ax,dx
        pop     ax
        jz      short pns3              ; jump if exact match

        putstr  " + "
        Pcall   OutHex                  ; print difference
        jmp     short pns3

pns2:   putstr  " + ??"
pns3:   test    cx,MAP_BRACKET
        jz      short pns4
        mov     al,']'                  ; print brackets around the symbol
        Pcall   putchar
pns4:   clc                             ; clear carry to signal success
pnsx:   RestReg <di, si>
EndProc

;***    ListMaps - List any symbol maps linked to the debugger.
;
;       "LM" command.  Prints on the debugger terminal the names
;       of the currently linked/active maps.
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;

DefProc ListMaps, NEAR, LOCAL
        Pcall   ScanAtom                ; get search map name
        mov     si,dataOFFSET AtomBuf
        cmp     byte ptr [si],0         ; anything on command line?
        jne     short lmp10
        xor     si,si                   ; all active maps
lmp10:  push    si
        Pcall   EachMap, <codeOFFSET PrintMapName, si, 0, 0>
        pop     si
        or      ax,ax
        jnz     short lmpx
        cmp     si,0
        jz      short lmp20
        putstr  <"Map not found", CR, LF>
        jmp     short lmpx
lmp20:  putstr  <"No maps loaded", CR, LF>
lmpx:
EndProc

;***    PrintMapName - Prints name of current map
;
;       NOTE:   This is an EachMap Worker Routine.
;
;       ENTRY:  (MapAddrPtr)    = pointer to MapAddr
;               (MapDefPtr)     = pointer to MapDef
;               (ArgPtr)        = pointer to argument structure
;
;       EXIT:   Carry clear, continue EachMap.
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc PrintMapName, NEAR, PUBLIC

ArgVar  MapAddrPtr,  WORD
ArgVar  MapDefPtr,   WORD
ArgVar  ArgPtr,      WORD

BeginProc
        mov     di,MapDefPtr            ; (DI) = pointer to MapDef
        lea     si,[di].MapNameLen      ; (SI) = pointer to map name packet
        Pcall   PrintPackLC             ; print Map name
        test    [di].MapFlags,MAP_FLAG_ACTIVE
        jz      short pmp10
        putstr  " is active"            ; if map is active, say so.
pmp10:  test    [di].MapFlags,MAP_FLAG_WINOS2
        jz      short pmp40
        .386p
        SaveReg <eax,esi,edi,ds>
        mov     eax,[di].MapPtr.AddrOff

        FlatContext ds,di
        mov     esi,NESymHead                ; (ESI) = Pointer to NESymStruc
pmp15:  or      esi,esi                      ; More NESymStruc's to check
        jz      short pmpie                  ;  Jmp if no
        movzx   edi,[esi].NE_cbModName       ;
        add     edi,size NESymStruc          ;
        add     edi,esi                      ; (EDI) = Pointer to MapDef
        cmp     eax,[edi].MapPtr.AddrOff     ; MapDef found
        jz      short pmp20                  ;  Jmp if yes
        mov     esi,[esi].NE_pNext           ; (ESI) = Next NESymStruc
        jmp     short pmp15                  ; Loop

pmp20:  mov     edi,[esi].NE_pSegTable       ; (EDI) = Pointer to SegTable
        or      edi,edi                      ; Pointer to SegTable valid
        jz      short pmpie                  ;  Jmp if no
        mov     esi,[edi].MapAddrS.AddrOff   ; Done so [ is outputed
pmp25:  mov     dx,[edi].MapAddrS.AddrTask   ; (DX)  = Slot number
        mov     edi,[edi].MapAddrS.AddrOff   ; (EDI) = Next SegTable
        DataContext ds,ax                    ;
        cmp     esi,edi                      ; First time through loop
        jnz     short pmp26                  ;  Jmp if no
        putstr  " ["                         ; Print [
pmp26:  Pcall   out16                        ; Print slot number
        or      edi,edi                      ; More SegTable's exist
        jz      short pmp30                  ;  Jmp if no
        mov     al,','                       ; Print ,
        Pcall   putchar                      ;
        FlatContext ds,ax                    ;
        jmp     short pmp25                  ; Loop

pmp30:  RestReg <ds,edi,esi,eax>
        .286p
    assumeDATA  ds
        putstr  "]"
pmp40:  Pcall   crlf
        xor     ax,ax                   ; clears carry, continue EachMap
        not     ax                      ; (AX) != 0, found symbol
EndProc
pmpie:  DataContext ds,ax
        InternalSymErr  <PrintMapName>

;***    ListGroups - Lists names of groups in currently active map.
;
;       LG [<mapname>].  Prints on the debugger terminal the names
;       of the groups in the currently active symbol maps.
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;

DefProc ListGroups, NEAR, LOCAL
        Pcall   ScanAtom                ; get search map name
        mov     si,dataOFFSET AtomBuf
        cmp     byte ptr [si],0         ; anything on command line?
        jne     short lgs10
        xor     si,si                   ; all active maps
lgs10:  Pcall   EachMap, <codeOFFSET EachMapListGroups, si, EACHMAP_ACTIVE, 0>
        or      ax,ax
        jnz     short lgsx
        putstr  <"Map not found", CR, LF>
lgsx:
EndProc

;***    EachMapListGroups - ListGroups's EachMap routine
;
;       NOTE:   This is an EachMap Worker Routine
;
;       ENTRY:  (MapAddrPtr)    = pointer to MapAddr
;               (MapDefPtr)     = pointer to MapDef
;               (ArgPtr)        = pointer to argument structure
;
;       EXIT:   (AX) != 0, symbol found
;               (AX) == 0, symbol not found
;               Carry clear, continue EachMap.
;               Carry set, halt EachMap.
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc EachMapListGroups, NEAR, LOCAL

ArgVar  MapAddrPtr, WORD
ArgVar  MapDefPtr,  WORD
ArgVar  ArgPtr,     WORD

BeginProc
        mov     di,MapDefPtr            ; (DI) = pointer to MapDef
        lea     si,[di].MapNameLen      ; (SI) = pointer to map name packet
        Pcall   PrintPackLC             ; print Map name
        putstr  <":", CR, LF>
        Pcall   EachGroup, <codeOFFSET PrintGroupName, \
                            [MapAddrPtr], [MapDefPtr], [ArgPtr]>
        xor     ax,ax                   ; clears carry, continue EachSym
        not     ax                      ; (AX) != 0, found symbol
EndProc

;***    PrintGroupName -  Print name of current group
;
;       NOTE:   This is an EachGroup Worker Routine
;
;       This routine is called from EachGroup and is used to
;       print the name of the current group.
;
;       ENTRY:  (GroupAddrPtr)  = pointer to GroupAddr
;               (GroupDefPtr)   = pointer to GroupDef
;               (ArgPtr)        = pointer to argument structure
;
;       EXIT:   Carry clear, continue EachGroup.
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc PrintGroupName, NEAR, LOCAL

ArgVar  GroupAddrPtr, WORD
ArgVar  GroupDefPtr,  WORD
ArgVar  ArgPtr,       WORD
LocVar  TmpAddr,      <size AddrS>

BeginProc
        mov     si,GroupDefPtr          ; (SI) = pointer to GroupDef
        lea     bx,TmpAddr              ; (BX) = AddrS to fill in
        Pcall   GetGroupAddr
        Pcall   OutAddrS, <bx>
        Pcall   blank
        lea     si,[si].GroupNameLen    ; (SI) = pointer to group name packet
        Pcall   PrintPack               ; print group name
        Pcall   crlf
        xor     ax,ax                   ; clears carry, continue EachGroup
        not     ax                      ; (AX) != 0, found symbol
EndProc

;***    ListSyms -  List all symbols in the specified group
;
;       "LS [<addr>]" command.  Prints on the debugger terminal the
;       symbols defined in the group whose name is specified in the
;       command line.
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;

DefProc ListSyms, NEAR, LOCAL

LocVar  ArgStruc, <size ArgS>
LocVar  Addr,     <size AddrS>

BeginProc
        lea     bx,Addr                 ; (BX) = AddrS pointer
        Pcall   FillDefAddr, <bx, RegSA.CSsav, RegSA.EIPsav.LowWord, \
                              RegSA.EIPsav.HighWord>
        lea     di,ArgStruc             ; (DI) = pointer to ArgStruc
        mov     [di].ArgAddrPtr,bx
        mov     [di].ArgFlags,0
        Pcall   EvalAddr, <bx, bx>
        mov     cx,1
        Pcall   AddrToLin               ; (AX:DX) = linear match value
        jc      short lsm10             ; if address invalid, sym not found
        mov     [di].ArgMatchValue.HighWord,ax
        mov     [di].ArgMatchValue.LowWord,dx
        Pcall   EachMap, <codeOFFSET EachMapListSyms, 0, EACHMAP_ACTIVE, di>
        or      ax,ax
        jnz     short lsmx
lsm10:  putstr  <"No symbols found", CR, LF>
lsmx:
EndProc

;***    EachMapListSyms - ListSyms' EachMap routine
;
;       NOTE:   This is an EachMap Worker Routine
;
;       ENTRY:  (MapAddrPtr)    = pointer to MapAddr
;               (MapDefPtr)     = pointer to MapDef
;               (ArgPtr)        = pointer to argument structure
;
;       EXIT:   (AX) != 0, symbol found
;               (AX) == 0, symbol not found
;               Carry clear, continue EachMap.
;               Carry set, halt EachMap.
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc EachMapListSyms, NEAR, LOCAL

ArgVar  MapAddrPtr, WORD
ArgVar  MapDefPtr,  WORD
ArgVar  ArgPtr,     WORD

BeginProc
        Pcall   EachGroup, <codeOFFSET PrintGroupSyms, \
                            [MapAddrPtr], [MapDefPtr], [ArgPtr]>
        cmp     ax,1                    ; clear carry if symbol found
        cmc                             ; set carry to halt EachMap
EndProc

;***    PrintGroupSyms - Prints symbols of Group if it matches search Group.
;
;       NOTE:   This is an EachGroup Worker Routine.
;
;       This routine is called from EachGroup and is used to compare
;       the current group to the search group and print the symbols
;       of the group if a match exists.
;
;       ENTRY:  (GroupAddrPtr)  = pointer to GroupAddr
;               (GroupDefPtr)   = pointer to GroupDef
;               (ArgPtr)        = pointer to argument structure
;
;       EXIT:   (AX) != 0, symbol found
;               (AX) == 0, symbol not found
;               Carry clear, continue EachGroup
;               Carry set, halt EachGroup
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc PrintGroupSyms, NEAR, LOCAL

ArgVar  GroupAddrPtr, WORD
ArgVar  GroupDefPtr,  WORD
ArgVar  ArgPtr,       WORD
LocVar  addrsTemp,    <size AddrS>

BeginProc
        lea     bx,[addrsTemp]          ; (BX) = AddrS
        mov     si,[GroupDefPtr]        ; (SI) = GroupDef pointer
        Pcall   GetGroupAddr
        mov     cx,1
        Pcall   AddrToLin               ; (AX:DX) = base value
        jc      short pgs30             ;  if error, skip group

        mov     di,[ArgPtr]             ; (DI) = Arg structure pointer
        mov     cx,[di].ArgMatchValue.LowWord ; (BX:CX) = match value
        mov     bx,[di].ArgMatchValue.HighWord

        cmp     bx,ax
        jb      short pgs30             ; if match value < base value, exit
        ja      short pgs10
        cmp     cx,dx
        jb      short pgs30             ; if match value < base value, exit

pgs10:  sub     cx,dx
        sbb     bx,ax                   ; (BX:CX) = match value - base value
        cmp     bx,[si].GroupEndValue.HighWord
        ja      short pgs30             ; if match value > end value, exit
        jb      short pgs20
        cmp     cx,[si].GroupEndValue.LowWord
        ja      short pgs30             ; if match value > end value, exit

;       Groups have matched, go print name of each symbol in current group.
;       If the group is 32 bit, then use EachSym32 to do the work.

pgs20:  test    [si].GroupSymSize,GRP_SIZE_32
        jnz     short pgs40             ; it is a 32-bit group
        Pcall   EachSym, <codeOFFSET PrintSymbol, GroupAddrPtr, si, di>
        stc                             ; set carry to halt EachGroup
        jmp     short pgsx              ; exit, symbol found, halt EachGroup

pgs30:  xor     ax,ax                   ; (AX) == 0, sym not found, carry clr
        jmp     short pgsx              ; exit

pgs40:  Pcall   EachSym32, <codeOFFSET PrintSym32, GroupAddrPtr, si, di>
        stc                             ; set carry to halt EachGroup
pgsx:
EndProc

;***    PrintSymbol - Prints the name of the current symbol
;
;       NOTE:   This is an EachSym Worker Routine.
;
;       This routine is used to print the name of the current symbol.
;
;       ENTRY:  (ArgPtr)      = Pointer to argument structure
;               (GroupDefPtr) = Pointer to GroupDef
;               (SymDefPtr)   = Pointer to SymDef
;
;       EXIT:   (AX) != 0, symbol found
;               (AX) == 0, symbol not found
;               Carry clear, continue EachSym
;               Carry set, halt EachSym
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc PrintSymbol, NEAR, LOCAL

ArgVar  ArgPtr,      WORD
ArgVar  GroupDefPtr, WORD
ArgVar  SymDefPtr,   WORD
LocVar  addrsTemp,   <size AddrS>

BeginProc
        mov     si,[GroupDefPtr]        ; (SI) = GroupDef pointer
        lea     bx,[addrsTemp]          ; (BX) = AddrS
        Pcall   GetGroupAddr
        mov     di,[SymDefPtr]          ; (DI) = pointer to SymDef
        Pcall   AddAddr, <[di].SymValue, 0>
        Pcall   OutAddrS, <bx>
        Pcall   blank
        lea     si,[di].SymNameLen      ; (SI) = pointer to symbol name packet
        Pcall   PrintPack               ; print the symbol name
        Pcall   crlf
        xor     ax,ax                   ; clears carry, continue EachSym
        not     ax                      ; (AX) != 0, found symbol
EndProc

;***    PrintSym32 - Prints the name of the current symbol
;
;       NOTE:   This is an EachSym32 Worker Routine.
;
;       This routine is used to print the name of the current symbol.
;
;       ENTRY:  (ArgPtr)      = Pointer to argument structure
;               (GroupDefPtr) = Pointer to GroupDef or MapDefPtr
;               (SymDefPtr)   = Pointer to SymDef32
;
;       EXIT:   (AX) != 0, symbol found
;               (AX) == 0, symbol not found
;               Carry clear, continue EachSym32
;               Carry set, halt EachSym32
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc PrintSym32, NEAR, LOCAL

ArgVar  ArgPtr,      WORD
ArgVar  GroupDefPtr, WORD
ArgVar  SymDefPtr,   WORD
LocVar  addrsTemp,   <size AddrS>

BeginProc
        mov     si,[GroupDefPtr]        ; (SI) = GroupDef pointer
        lea     bx,[addrsTemp]          ; (BX) = AddrS
        Pcall   GetGroupAddr
        mov     di,[SymDefPtr]          ; (DI) = pointer to SymDef
        Pcall   AddAddr, <[di].SymValue32.LowWord, [di].SymValue32.HighWord>
        Pcall   OutAddrS, <bx>
        Pcall   blank
        lea     si,[di].SymNameLen32    ; (SI) = pointer to symbol name packet
        Pcall   PrintPack               ; print the symbol name
        Pcall   crlf
        xor     ax,ax                   ; clears carry, continue EachSym
        not     ax                      ; (AX) != 0, found symbol
EndProc

;***    ListAbsSyms -  List all absolute symbols in the active map.
;
;       "LA [<mapname>]" command.  Prints on the debugger terminal the
;       absolute symbols in the map.
;
;       ENTRY:  NONE
;
;       EXIT:   NONE
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;

DefProc ListAbsSyms, NEAR, LOCAL
        Pcall   ScanAtom                ; get search map name
        mov     si,dataOFFSET AtomBuf
        cmp     byte ptr [si],0         ; anything on command line?
        jne     short lab10
        xor     si,si                   ; all active maps
lab10:  Pcall   EachMap, <codeOFFSET EachMapListAbsSyms, si, EACHMAP_ACTIVE, 0>
        or      ax,ax
        jnz     short labx
        putstr  <"Map not found", CR, LF>
labx:
EndProc

;***    EachMapListAbsSyms - ListAbsSyms' EachMap routine
;
;       NOTE:   This is an EachMap Worker Routine
;
;       ENTRY:  (MapAddrPtr)    = pointer to MapAddr
;               (MapDefPtr)     = pointer to MapDef
;               (ArgPtr)        = pointer to argument structure
;
;       EXIT:   (AX) != 0, symbol found
;               (AX) == 0, symbol not found
;               Carry clear, continue EachMap.
;               Carry set, halt EachMap.
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc EachMapListAbsSyms, NEAR, LOCAL

ArgVar  MapAddrPtr, WORD
ArgVar  MapDefPtr,  WORD
ArgVar  ArgPtr,     WORD

BeginProc
        mov     di,MapDefPtr            ; (DI) = pointer to MapDef
        lea     si,[di].MapNameLen      ; (SI) = pointer to map name packet
        Pcall   PrintPackLC             ; print Map name
        putstr  <":", CR, LF>
        mov     di,MapDefPtr            ; (DI) = pointer to MapDef
        test    [di].MapAbsSize,GRP_SIZE_32
        jnz     short las10
        Pcall   EachAbsSym, <codeOFFSET PrintAbsSym, \
                            [MapAddrPtr], [MapDefPtr], [ArgPtr]>
        jmp     short las20

las10:  Pcall   EachAbsSym32, <codeOFFSET PrintAbsSym32, \
                              [MapAddrPtr], [MapDefPtr], [ArgPtr]>
las20:  xor     ax,ax                   ; clears carry, continue EachSym
        not     ax                      ; (AX) != 0, found symbol
EndProc

;***    PrintAbsSym - Prints the name of the current symbol
;
;       NOTE:   This is an EachAbsSym Worker Routine.
;
;       This routine is used to print the name of the current symbol.
;
;       ENTRY:  (ArgPtr)    = Pointer to argument structure
;               (MapDefPtr) = Pointer to MapDefPtr
;               (SymDefPtr) = Pointer to SymDef
;
;       EXIT:   (AX) != 0, symbol found
;               (AX) == 0, symbol not found
;               Carry clear, continue EachAbsSym
;               Carry set, halt EachAbsSym
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc PrintAbsSym, NEAR, LOCAL

ArgVar  ArgPtr,      WORD
ArgVar  GroupDefPtr, WORD
ArgVar  SymDefPtr,   WORD

BeginProc
        mov     bx,[SymDefPtr]          ; (BX) = pointer to SymDef
        mov     dx,[bx].SymValue        ; (DX) = symbol Value
        Pcall   out16                   ; print the symbol value
        Pcall   blank
        lea     si,[bx].SymNameLen      ; (SI) = pointer to symbol name packet
        Pcall   PrintPack               ; print the symbol name
        Pcall   crlf
        xor     ax,ax                   ; clears carry, continue EachSym
        not     ax                      ; (AX) != 0, found symbol
EndProc

;***    PrintAbsSym32 - Prints the name of the current symbol
;
;       NOTE:   This is an EachAbsSym32 Worker Routine.
;
;       This routine is used to print the name of the current symbol.
;
;       ENTRY:  (ArgPtr)    = Pointer to argument structure
;               (MapDefPtr) = Pointer to MapDefPtr
;               (SymDefPtr) = Pointer to SymDef32
;
;       EXIT:   (AX) != 0, symbol found
;               (AX) == 0, symbol not found
;               Carry clear, continue EachAbsSym32
;               Carry set, halt EachAbsSym32
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags

DefProc PrintAbsSym32, NEAR, LOCAL

ArgVar  ArgPtr,      WORD
ArgVar  GroupDefPtr, WORD
ArgVar  SymDefPtr,   WORD

BeginProc
        mov     bx,[SymDefPtr]          ; (BX) = pointer to SymDef
        mov     ax,[bx].SymValue32.HighWord
        mov     dx,[bx].SymValue32.LowWord   ; (AX:DX) = symbol Value
        Pcall   Out32                   ; print the symbol value
        Pcall   blank
        lea     si,[bx].SymNameLen32    ; (SI) = pointer to symbol name packet
        Pcall   PrintPack               ; print the symbol name
        Pcall   crlf
        xor     ax,ax                   ; clears carry, continue EachSym
        not     ax                      ; (AX) != 0, found symbol
EndProc

;***    EachMap - On each map in the debugger, execute worker
;
;       Pcall EachMap, <WorkerAddr, MapNamePtr, flEachMap, ArgPtr>
;
;       EachMap will take the Worker routine address passed to it and
;       then perform the Worker routine on each map linked in the debugger
;
;       ENTRY:  (WorkerAddr) = code OFFSET of worker routine
;               (MapNamePtr) = pointer to map name to match or
;                                NULL if all (active) maps.
;               (flEachMap)  = each map flags
;                                EACHMAP_ACTIVE      - only active maps
;                                EACHMAP_START_LAST  - start at last map
;                               TRUE all maps, FALSE all active maps
;               (ArgPtr)     = pointer to argument structure
;
;       EXIT:   (AX) != 0, symbol, group or map found
;               (AX) == 0, symbol, group or map not found
;               Carry clear, if all maps passed to worker.
;               Carry set, if maps returns carry set, to halt EachMap.
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;

DefProc EachMap, NEAR, PUBLIC

ArgVar  WorkerAddr,   WORD
ArgVar  MapNamePtr,   WORD
ArgVar  flEachMap,    WORD
ArgVar  ArgPtr,       WORD
LocVar  cMap,         WORD
LocVar  Addr,         <size AddrS>
LocVar  MapDefBuff,   <(size MapDef) + MAX_NAME_SIZE>

BeginProc
        mov     ax,[cMapCount]
        mov     [cMap],ax               ; initialize map count
        xor     ax,ax                   ; (AX) = no map found
        mov     si,dataOFFSET addrsLastMap ; (SI) = map symbol last found in
        test    [flEachMap],EACHMAP_START_LAST
        jnz     short eam10
        mov     si,dataOFFSET addrsFirstMap ; (SI) = first map in list

eam10:  lea     bx,[Addr]               ; (BX) = pointer to Map AddrS
        Pcall   CopyAddr, <si, bx>
        lea     di,MapDefBuff           ; (DI) = pointer to MapDef buffer
        push    ax
        Pcall   GetMapDefErr            ; get MapDef
        pop     ax
        jc      short eam50             ; exit if error in GetMapDef

;       (BX) = MapAddrPtr, (DI) = MapDefPtr

        mov     si,[MapNamePtr]         ; (SI) = name string pointer
        or      si,si                   ; is map name null?
        je      short eam20             ;  yes, do all active maps
        lea     di,[di].MapNameLen      ; (DI) = pointer to current Map name
        Pcall   CompPacket              ; compare name packets
        jc      short eam30             ; jump if not equal
        lea     di,MapDefBuff           ; (DI) = pointer to MapDef buffer
        mov     [pMapDefCur],di         ; set up MapDef pointer
        Pcall   WorkerAddr, <bx, di, [ArgPtr]>
        stc
        jmp     short eam50             ; jump to exit

eam20:  test    [flEachMap],EACHMAP_ACTIVE ; each active map?
        jz      short eam25               ;  no, all maps
        test    [di].MapFlags,MAP_FLAG_ACTIVE
        jz      short eam30
eam25:  mov     [pMapDefCur],di         ; set up MapDef pointer
        Pcall   WorkerAddr, <bx, di, [ArgPtr]>
        jc      short eam50             ; exit if worker returns carry set.

eam30:  lea     si,MapDefBuff.MapAddrS  ; (SI) = pointer to MapDef AddrS
        cmp     [si].AddrType,0
        jne     short eam40             ; jump if not end of map linked list
        mov     si,dataOFFSET addrsFirstMap ; wrap around to first map
eam40:  dec     [cMap]
        jnz     short eam10
        clc                             ; clear carry for case of no more maps
eam50:  mov     [pMapDefCur],-1         ; invalidate MapDef pointer
        pushf
        or      ax,ax
        jz      short eam60
        lea     bx,[Addr]
        Pcall   CopyAddr, <bx, dataOFFSET addrsLastMap>
eam60:  POPFF
EndProc

;***    UpdateWinOS2Maps - Update the SegTables for all WINOS2 maps
;
;       ENTRY: NONE
;
;       EXIT:  NONE
;
;       USES:  Flags
;

.386p
DefProc UpdateWinOS2Maps, NEAR, PUBLIC

BeginProc
        pushad
        SaveReg <ds,es>
        whatmode
        jnc     uwmx
        mov     bx,CurrTask                  ; (BX)= Curr task num

        FlatContext <ds,es>,ax
        mov     edi,NESymHead                ; (EDI) = pNESymStruc
uwm10:  or      edi,edi                      ; More NESymStruc's to check
        jz      short uwmx                   ;  Jmp if no
        cmp     [edi].NE_SlotCurrSegTable,bx ; Is the current segtable correct
        jnz     short uwm20                  ;  Jmp if no
uwm15:  mov     edi,[edi].NE_pNext           ; (EDI) = pNESymStruc
        jmp     short uwm10                  ; Loop

    ; EDI - Pointer to NESymStruc to deactivate
    ; EDX - Pointer to MapDef to deactivate

uwm18:  mov     [edi].NE_SlotCurrSegTable,0
        and     [edx].MapFlags,not MAP_FLAG_ACTIVE
        jmp     short uwm15

    ; EDI - Pointer to NESymStruc to update

uwm20:  movzx   edx,[edi].NE_cbModName       ;
        add     edx,size NESymStruc          ;
        add     edx,edi                      ; (EDX) = Pointer to MapDef
        mov     esi,[edi].NE_pSegTable       ; (ESI) = Pointer to SegTable
uwm30:  or      esi,esi                      ; More seg tables to check
        jz      short uwm18                  ;  Jmp if no
        cmp     [esi].MapAddrS.AddrTask,bx   ; Seg table found
        jz      short uwm40                  ;  Jmp if yes
        mov     esi,[esi].MapAddrS.AddrOff   ; Check next seg table
        jmp     short uwm30                  ; Loop

uwm40:  push    [esi].MapAddrS.AddrTask
        push    [esi].MapAddrS.AddrOff
        mov     [esi].MapAddrS.AddrOff,edx
        mov     [esi].MapAddrS.AddrType,SYM_TYPE_LIN
        mov     [esi].MapAddrS.AddrTask,0
        call    ReLinkWinOS2Map              ; Update SegTables
        pop     [esi].MapAddrS.AddrOff
        pop     [esi].MapAddrS.AddrTask

        mov     [edi].NE_SlotCurrSegTable,bx
        or      [edx].MapFlags,MAP_FLAG_ACTIVE
        jmp     short uwm15                  ; Loop

uwmx:   RestReg <es,ds>
    assumeDATA <ds,es>
        popad
EndProc

;***    ReLinkWinOS2Map - Worker for UpdateWINOS2Maps
;
;       ENTRY:  ESI = Pointer to SegTable
;
;       EXIT:   NONE
;
;       USES:   Flags
;

DefProc ReLinkWinOS2Map, NEAR, LOCAL

LocVar  MapDefBuff,  <(size MapDef) + MAX_NAME_SIZE>    ; for SegDefLinkUp
LocVar  SegTableBuf, <size SegTable>                    ;
LocVar  ParmAddr,    <size AddrS>                       ;
LocVar  ArgStruc,    <size ArgS>                        ;

BeginProc
        pushad
        SaveReg <ds,es>
        mov     ax,ds
        DataContext <ds,es>
        lea     bx,ParmAddr             ; (BX) = Parameter frame AddrS
        mov     ecx,esi
        shr     ecx,16
        Pcall   FillDefAddr, <bx, ax, si, cx>
        call    SegDefLinkUp1
        jc      short rlwe
        RestReg <es,ds>
        popad
EndProc
rlwe:   InternalSymErr  <ReLinkWinOS2Map>
.286p

;***    EachGroup - On each group in the map given, perform a specified task
;
;       Pcall EachGroup, <WorkerAddr, MapAddrPtr, MapDefPtr, ArgPtr>
;
;       EachGroup will take the Worker routine address passed to it and the
;       MapAddr passed to it and then perform the Worker Routine on each
;       group in the map specified.
;
;       ENTRY:  (WorkerAddr)     = code OFFSET of worker routine
;               (MapAddrPtr)     = pointer to MapAddr
;               (MapDefPtr)      = pointer to MapDef
;               (ArgPtr)         = pointer to argument structure
;
;       EXIT:   (AX) != 0, symbol or group found
;               (AX) == 0, symbol or group not found
;               Carry clear, if all groups passed to worker.
;               Carry set, if worker returns carry set, to halt EachGroup.
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;

DefProc EachGroup, NEAR, PUBLIC

ArgVar  WorkerAddr,     WORD
ArgVar  MapAddrPtr,     WORD
ArgVar  MapDefPtr,      WORD
ArgVar  ArgPtr,         WORD
LocVar  GroupDefCount,  WORD
LocVar  ReturnValue,    WORD
LocVar  GroupDefBuff,   <(size GroupDef) + MAX_NAME_SIZE>
LocVar  GroupAddr,      <size AddrS>

BeginProc
        mov     ReturnValue,0           ; symbol not found
        mov     si,MapDefPtr            ; (SI) = pointer to MapDef buffer
        mov     cx,[si].GroupCnt        ; (CX) = number of groups in map
        jcxz    short egp30             ; exit if no maps
        mov     GroupDefCount,cx
        mov     dx,[si].GroupChain      ; (DX) = segment link to first group
        lea     di,GroupDefBuff         ; (DI) = pointer to GroupDef buffer
        lea     bx,GroupAddr            ; (BX) = pointer to group AddrS

egp10:  Pcall   ConvertPara2Off         ; (ax:dx) = GroupChain offset

;       Copy map AddrS to group AddrS and add group offset
;       from start of map.

        Pcall   CopyAddr, <MapAddrPtr, bx>
        Pcall   AddAddr, <dx, ax>

;       Get the group name length

        Pcall   GetByteOff, <GroupNameLen, 0> ; (AL) = group name length
        jc      short egpe
        cmp     al,MAX_NAME_SIZE
        jbe     short egp20
        mov     al,MAX_NAME_SIZE

egp20:  add     al,size GroupDef
        mov     cl,al
        xor     ch,ch                   ; (CX) = size of GroupDef and Name
        Pcall   GetBuff                 ; Get Group Def and Group Name
        jc      short egpe

;       Execute worker routine
;       (BX) = GroupAddrPtr, (DI) = GroupDefPtr

        Pcall   WorkerAddr, <bx, di, ArgPtr>
        mov     ReturnValue,ax
        jc      short egpx

        lea     bx,GroupAddr            ; (BX) = pointer to group AddrS
        lea     di,GroupDefBuff         ; (DI) = pointer to GroupDef buffer
        mov     dx,[di].GroupLink       ; (DX) = segment Link to next Group
        dec     GroupDefCount
        jnz     egp10                   ; next group
egp30:  clc                             ; clear carry to signal all groups done
egpx:   mov     ax,ReturnValue
EndProc
egpe:   InternalSymErr  <EachGroup>

;***    EachAbsSym - on each absolute symbol in a map, perform a specified task
;
;       Pcall EachAbsSym, <WorkerAddr, MapAddrPtr, MapDefPtr, ArgPtr>
;
;       EachAbsSym will take the parameters passed to it and then perform
;       Worker routine on each Absolute Symbol in the given Map.
;
;       ENTRY:  (WorkerAddr)    = code OFFSET of worker routine
;               (MapAddrPtr)    = pointer to MapAddr
;               (MapDefPtr)     = pointer to MapDef
;               (ArgPtr)        = pointer to argument structure
;
;       EXIT:   (AX) != 0, symbol found
;               (AX) == 0, symbol not found
;               Carry clear, if all absolute symbols passed to worker.
;               Carry set, if worker returns carry set, to halt EachAbsSym.
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;

DefProc EachAbsSym, NEAR, PUBLIC

ArgVar  WorkerAddr,  WORD               ; Frame must be the same as EachSym
ArgVar  MapAddrPtr,  WORD               ; Must be same place as GroupAddrPtr
ArgVar  MapDefPtr,   WORD               ; Must be same place as GroupDefPtr
ArgVar  ArgPtr,      WORD
LocVar  SymCount,    WORD
LocVar  SymAddr,     <size AddrS>

BeginProc
        xor     ax,ax                   ; (AX) = symbol not found
        mov     di,MapDefPtr            ; get number of AbsSyms from MapDef
        mov     cx,[di].ConCnt
        jcxz    short eab1              ; exit if no AbsSyms
        mov     SymCount,cx

        mov     si,MapAddrPtr
        lea     bx,SymAddr
        Pcall   CopyAddr, <si, bx>      ; copy MapAddr to SymAddr

;       Add the size of the header SymAddr in front of MapDef

        Pcall   AddAddr, <offset MapDefStart, 0>
        mov     cx,[di].ConChain        ; (CX) = offset to abs chain
        Pcall   GetWordOff, <cx, 0>     ; (DX) = start of abs chain
        jc      short eabe              ; jump if error, (AX) = error code
        xor     ax,ax                   ; (AX) = symbol not found
        jmp     EachSymSub              ; jump to common code in EachSym

eab1:   clc                             ; all syms completed, exit carry clear
EndProc
eabe:   InternalSymErr  <EachAbsSym>

;***    EachAbsSym32 - on each abs symbol in a map, perform a specified task
;
;       Pcall EachAbsSym32, <WorkerAddr, MapAddrPtr, MapDefPtr, ArgPtr>
;
;       EachAbsSym32 will take the parameters passed to it and then perform
;       Worker routine on each Absolute Symbol in the given Map.
;
;       ENTRY:  (WorkerAddr)    = code OFFSET of worker routine
;               (MapAddrPtr)    = pointer to MapAddr
;               (MapDefPtr)     = pointer to MapDef
;               (ArgPtr)        = pointer to argument structure
;
;       EXIT:   (AX) != 0, symbol found
;               (AX) == 0, symbol not found
;               Carry clear, if all absolute symbols passed to worker.
;               Carry set, if worker returns carry set, to halt EachAbsSym32.
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;

DefProc EachAbsSym32, NEAR, PUBLIC

ArgVar  WorkerAddr,  WORD               ; Frame must be the same as EachSym32
ArgVar  MapAddrPtr,  WORD               ; Must be same place as GroupAddrPtr
ArgVar  MapDefPtr,   WORD               ; Must be same place as GroupDefPtr
ArgVar  ArgPtr,      WORD
LocVar  SymCount,    WORD
LocVar  SymAddr,     <size AddrS>

BeginProc
        xor     ax,ax                   ; (AX) = symbol not found
        mov     di,MapDefPtr            ; get number of AbsSyms from MapDef
        mov     cx,[di].ConCnt
        jcxz    short eas1              ; exit if no AbsSyms
        mov     SymCount,cx

        mov     si,MapAddrPtr
        lea     bx,SymAddr
        Pcall   CopyAddr, <si, bx>      ; copy MapAddr to SymAddr

;       Add the size of the header SymAddr in front of MapDef

        Pcall   AddAddr, <offset MapDefStart, 0>
        mov     cx,[di].ConChain        ; (CX) = offset to abs chain
        Pcall   GetWordOff, <cx, 0>     ; (DX) = start of abs chain
        jc      short ease              ; jump if error, (AX) = error code
        xor     ax,ax                   ; (AX) = symbol not found
        jmp     EachSym32Sub            ; jump to common code in EachSym

eas1:   clc                             ; all syms completed, exit carry clear
EndProc
ease:   InternalSymErr  <EachAbsSym32>

;***    EachSym - On each symbol in the given group, perform, a specified task
;
;       Pcall EachSym, <WorkerAddr, GroupAddrPtr, GroupDefPtr, ArgPtr>
;
;       EachSym will take the parameters passed to it and then perform the
;       Worker routine on each symbol in the specified GroupDef.
;
;       ENTRY:  (WorkerAddr)    = code OFFSET of worker routine
;               (GroupAddrPtr)  = pointer to GroupAddr
;               (GroupDefPtr)   = pointer to GroupDef
;               (ArgPtr)        = pointer to argument structure
;
;       EXIT:   (AX) != 0, symbol found
;               (AX) == 0, symbol not found
;               Carry clear, if all symbols passed to worker.
;               Carry set, if worker returns carry set, to halt EachSym.
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;

DefProc EachSym, NEAR, PUBLIC

ArgVar  WorkerAddr,   WORD              ; Frame must be the same as EachAbsSym
ArgVar  GroupAddrPtr, WORD              ; Must be same place as MapAddrPtr
ArgVar  GroupDefPtr,  WORD              ; Must be same place as MapDefPtr
ArgVar  ArgPtr,       WORD
LocVar  SymCount,     WORD
LocVar  SymAddr,      <size AddrS>

BeginProc
        xor     ax,ax                   ; symbol not found
        mov     di,GroupDefPtr          ; get number of symbols from GroupDef
        mov     cx,[di].SymCnt
        mov     SymCount,cx

        xor     dh,dh                   ; get DX as Offset from GroupAddr
        mov     dl,[di].GroupNameLen    ; to start of Symbols
        add     dx,(size GroupDef)
        lea     bx,SymAddr
        Pcall   CopyAddr, <GroupAddrPtr, bx> ; copy GroupAddr to SymAddr
        jmp     short esm2

esm1:   lea     bx,SymAddr              ; (BX) = AddrS pointer
        mov     dx,MEMCACHESIZE
        sub     dx,cx

Entry   EachSymSub, NEAR, LOCAL

;       (AX) = return code
;       (BX) = AddrS pointer
;       (DX) = initial offset for above AddrS

esm2:   Pcall   AddAddr, <dx, 0>        ; add DX offset to SymAddr
        mov     dx,MEMCACHESIZE         ; set up to get a full cache
        push    ax
        Pcall   GetCache                ; (DI) = cache buffer pointer
        jc      short esme              ; jump if error, (AX) = error code
        pop     ax                      ; (AX) = return code

;       (CX) = number of bytes in cache
;       (DI) = pointer to cached bytes

esm3:   xor     bh,bh
        mov     bl,[di].SymNameLen
        add     bx,(size SymDef)        ; (BX) = number of bytes in SymDef
        lea     dx,[bx].SymNameLen      ; (DX) = num bytes to next SymNameLen
        cmp     cx,dx
        jbe     esm1                    ; jump if not enough room in cache

        sub     cx,bx                   ; (CX) = number of bytes left in cache
        add     bx,di                   ; (BX) = point to next SymDef in cache
        push    bx                      ; save next SymDef pointer
        push    cx                      ; save cache byte count
        Pcall   WorkerAddr, <ArgPtr, GroupDefPtr, di>
        pop     cx                      ; (CX) = cache byte count
        pop     di                      ; (DI) = next SymDef pointer
        jc      short esmx              ; halt EachSym if carry set

        dec     SymCount                ; is there more symbols?
        jnz     short esm3              ; jump if more symbols
esm4:   clc                             ; all syms completed, exit carry clear
esmx:
EndProc
esme:   InternalSymErr  <EachSym/EachAbsSym>

;***    EachSym32 - On each symbol in the given group, perform, a specified task
;
;       Pcall EachSym32, <WorkerAddr, GroupAddrPtr, GroupDefPtr, ArgPtr>
;
;       EachSym32 will take the parameters passed to it and then perform the
;       Worker routine on each symbol in the specified GroupDef.  This routine
;       is the 32 bit equivalent to EachSym.
;
;       ENTRY:  (WorkerAddr)    = code OFFSET of worker routine
;               (GroupAddrPtr)  = pointer to GroupAddr
;               (GroupDefPtr)   = pointer to GroupDef
;               (ArgPtr)        = pointer to argument structure
;
;       EXIT:   (AX) != 0, symbol found
;               (AX) == 0, symbol not found
;               Carry clear, if all symbols passed to worker.
;               Carry set, if worker returns carry set, to halt EachSym.
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;

DefProc EachSym32, NEAR, PUBLIC

ArgVar  WorkerAddr,   WORD              ; Frame must be the same as EachAbsSym32
ArgVar  GroupAddrPtr, WORD              ; Must be same place as MapAddrPtr
ArgVar  GroupDefPtr,  WORD              ; Must be same place as MapDefPtr
ArgVar  ArgPtr,       WORD
LocVar  SymCount,     WORD
LocVar  SymAddr,      <size AddrS>

BeginProc
        xor     ax,ax                   ; symbol not found
        mov     di,GroupDefPtr          ; get number of symbols from GroupDef
        mov     cx,[di].SymCnt
        mov     SymCount,cx

        xor     dh,dh                   ; get DX as Offset from GroupAddr
        mov     dl,[di].GroupNameLen    ; to start of Symbols
        add     dx,(size GroupDef)
        lea     bx,SymAddr
        Pcall   CopyAddr, <GroupAddrPtr, bx> ; copy GroupAddr to SymAddr
        jmp     short ehs2

ehs1:   lea     bx,SymAddr              ; (BX) = AddrS pointer
        mov     dx,MEMCACHESIZE
        sub     dx,cx

Entry   EachSym32Sub, NEAR, LOCAL

;       (AX) = return code
;       (BX) = AddrS pointer
;       (DX) = initial offset for above AddrS

ehs2:   Pcall   AddAddr, <dx, 0>        ; add DX offset to SymAddr
        mov     dx,MEMCACHESIZE         ; set up to get a full cache
        push    ax
        Pcall   GetCache                ; (DI) = cache buffer pointer
        jc      short ehse              ; jump if error, (AX) = error code
        pop     ax                      ; (AX) = return code

;       (CX) = number of bytes in cache
;       (DI) = pointer to cached bytes

ehs3:   xor     bh,bh
        mov     bl,[di].SymNameLen32
        add     bx,(size SymDef32)      ; (BX) = number of bytes in SymDef32
        lea     dx,[bx].SymNameLen32    ; (DX) = num bytes to next SymNameLen
        cmp     cx,dx
        jbe     ehs1                    ; jump if not enough room in cache

        sub     cx,bx                   ; (CX) = number of bytes left in cache
        add     bx,di                   ; (BX) = point to next SymDef in cache
        push    bx                      ; save next SymDef pointer
        push    cx                      ; save cache byte count
        Pcall   WorkerAddr, <ArgPtr, GroupDefPtr, di>
        pop     cx                      ; (CX) = cache byte count
        pop     di                      ; (DI) = next SymDef pointer
        jc      short ehsx              ; halt EachSym if carry set

        dec     SymCount                ; are there more symbols?
        jnz     short ehs3              ; jump if more symbols
ehs4:   clc                             ; all syms completed, exit carry clear
ehsx:
EndProc
ehse:   InternalSymErr  <EachSym32/EachAbsSym32>

;***    EachSegDef - On each SegDef in the parameter frame, perform a task
;
;       Pcall EachSegDef, <WorkerAddr, GroupAddrPtr, GroupDefPtr, ArgPtr>
;
;       EachSegDef will take the parameters passed to it and then perform the
;       Worker routine on each segdef in the parameter frame.
;
;       ENTRY:  (WorkerAddr)    = code OFFSET of worker routine
;               (GroupAddrPtr)  = pointer to GroupAddr
;               (GroupDefPtr)   = pointer to GroupDef
;               (ArgPtr)        = pointer to argument structure
;
;       EXIT:   Carry clear, if all SegDefs passed to worker.
;               Carry set, if Worker returns Carry set, to halt EachSegDef.
;
;       USES:   AX, BX, CX, DX, SI, DI, Flags
;

DefProc EachSegDef, NEAR, PUBLIC

ArgVar  WorkerAddr,   WORD
ArgVar  GroupAddrPtr, WORD
ArgVar  GroupDefPtr,  WORD
ArgVar  ArgPtr,       WORD
LocVar  SegDefCount,  WORD
LocVar  SegDefAddr,   <size AddrS>
LocVar  SegDefBuff,   <size SegDef>

BeginProc
        mov     di,ArgPtr               ; (DI) = pointer to ArgStruc
        mov     di,[di].ArgAddrPtr      ; (DI) = pointer to ParmFrame AddrS
        lea     bx,SegDefAddr           ; (BX) = pointer to SegDef AddrS
        Pcall   CopyAddr, <di, bx>      ; Copy Parmframe AddrS to SegDef AddrS

;       Get number of SegDefs in parameter frame, store in (CX)

        Pcall   GetWordOff, <GroupCount, 0>
        jc      short esde
        mov     SegDefCount,dx
        mov     dx,offset SegDefs       ; (DX) = offset from parm to SegDefs

esd1:   Pcall   AddAddr, <dx, 0>        ; (BX) = pointer to SegDef AddrS
        mov     cx,size SegDef          ; (CX) = size of SegDef
        lea     di,SegDefBuff           ; (DI) = pointer to SegDef buffer
        Pcall   GetBuff
        jc      short esde

;       Execute worker routine, (DI) = SegDefBuff

        Pcall   WorkerAddr, <GroupAddrPtr, GroupDefPtr, di, ArgPtr>
        jc      short esdx              ; exit if worker set carry

        lea     bx,SegDefAddr           ; (BX) = pointer to SegDef addr
        mov     dx,size SegDef          ; (DX) = offset to next SegDef
        dec     SegDefCount
        jnz     short esd1              ; go do next SegDef
        clc                             ; clear carry to signal all SegDefs done
esdx:
EndProc
esde:   InternalSymErr  <EachSegDef>

;***    GetMapDefErr - Get the MapDef into Local buffer.
;
;       Pcall GetMapDefErr
;
;       GetMapDefErr will create an AddrS structure pointing to
;       a map and copy the MapDef from this AddrS to a LOCAL buffer
;       for the worker routines.
;
;       ENTRY:  (DI) = Offset of local buffer to store MapDef in.
;               (BX) = Pointer to MapAddr Address Structure.
;
;       EXIT:   Carry clear, if map exists
;               Carry set, if no map linked
;
;       USES:   AX, CX, DX, Flags
;

DefProc GetMapDefErr, NEAR, LOCAL
        cmp     [bx].AddrType,0         ; check for no map
        stc
        je      short gmex              ; jump if no map exists

;       Get map name length

        Pcall   GetByteOff, <MapNameLen, 0> ; (AL) = map name length
        jc      short gmee
        cmp     al,MAX_NAME_SIZE
        jbe     short gme10
        mov     al,MAX_NAME_SIZE

gme10:  add     al,(size MapDef)
        mov     cl,al
        xor     ch,ch                   ; (CX) = size of MapDef and map name
        Pcall   GetBuff                 ; get MapDef and MapName
        jc      short gmee
gmex:
EndProc
gmee:   InternalSymErr  <GetMapDefErr>

;***    GetGroupAddr - Fill in an AddrS with the group address
;
;       ENTRY:  (SI) = pointer to GroupDef
;               (BX) = pointer to AddrS
;
;       EXIT:   NONE
;
;       USES:   AX, CX, DX, DI, Flags

DefProc GetGroupAddr, NEAR, LOCAL
        xor     ax,ax
        xor     dx,dx
        xor     di,di
        mov     cl,[si].GroupAddrType   ; (CX) = address type
        test    [si].GroupSymSize,GRP_SIZE_32
        mov     ch,EXPR_SIZE_WORD       ; size usually a word
        jz      short gga10             ; 16bit map (or BIMODAL)
        mov     ch,EXPR_SIZE_DWORD      ; 32bit map, size is a dword

gga10:  cmp     cl,SYM_TYPE_BIMODAL     ; is this a bimodal GroupDef?
        jne     short gga20             ; if not bimodal, continue

        Pcall   WhatIsClientMode        ; is debuggee in PROT mode?
        mov     di,[si].GroupAddrSel    ; get selector value
        mov     cl,EXPR_TYPE_SEL
        jc      short gga50             ; if carry set, protected mode

        mov     di,[si].GroupAddrSeg    ; get segment value
        mov     cl,EXPR_TYPE_SEG
        jmp     short gga50             ; now exit

gga20:  cmp     cl,EXPR_TYPE_SEL        ; selector type?
        je      short gga30             ;  yes, get selector
        cmp     cl,EXPR_TYPE_SEG        ; real mode segment type?
        je      short gga40             ;  yes, get segment
        mov     ax,[si].GroupAddrLinPhy.HighWord
        mov     dx,[si].GroupAddrLinPhy.LowWord
        jmp     short gga50             ; otherwise linear or physical address

gga30:  mov     di,[si].GroupAddrSel
        jmp     short gga50

gga40:  mov     di,[si].GroupAddrSeg
gga50:  Pcall   FillAddr, <bx, di, dx, ax, [si].GroupTaskNum, cx>
EndProc

;***    ConvertPara2Off - convert paragraph to offset
;
;       ENTRY:  (DX)    = paragraph address
;
;       EXIT:   (AX:DX) = adjusted offset
;
;       USES:   AX, DX, Flags

DefProc ConvertPara2Off, NEAR, LOCAL
        rol     dx,4                    ; shift over & drop high 4 bits into dl
        mov     al,dl
        and     ax,0fh                  ; (ax) = high 4 bits
        and     dl,0f0h                 ; (ax:dx) = paragraph << 4
        add     dx,offset MapDefStart
        adc     ax,0
EndProc

;***    CompPacket - Compare two symbol packets
;
;       Is a "don't care" compare regarding case for symbol in
;       the map.  This is because the debugger upshifts all
;       symbols, but we want to conserve the visually distinct
;       nature of symbols that combine upper and lower case.
;
;       ENTRY:  (SI) = pointer to search symbol name packet.
;               (DI) = pointer to symbol in map to compare with.
;
;       EXIT:   Carry clear, if packets are the same.
;               Carry set, if packets are not the same.
;
;       USES:   Flags
;

DefProc CompPacket, NEAR, LOCAL
        SaveReg <ax, cx, si, di>
        cmpsb                           ; Compare lengths
        stc                             ; In case they aren't the same.
        jne     cmpx                    ; If they didn't match.

        xor     ch,ch                   ; Clear upper part
        mov     cl,[si - 1]             ; Get symbol name-length
        jcxz    cmpx
        xchg    si, di                  ; swap for lodsb & scasb

cmp1:   lodsb                           ; (al) = the symbol's char.
        cmp     al, 'a'
        jb      short cmp2
        cmp     al, 'z'
        ja      short cmp2
        sub     al, 'a' - 'A'           ; (al) = upper case letter
cmp2:   scasb                           ; Do they compare?
        loope   cmp1                    ; Continue?

        stc
        jne     cmpx                    ; If they didn't compare
        clc
cmpx:   RestReg <di, si, cx, ax>
EndProc

;***    CopySymbol - Copy a symbol name into the passed buffer (null terminate)
;
;       ENTRY   (SI) = symbol name packet
;               (DI) = buffer
;
;       EXIT    (DI) = updated buffer pointer (points to null terminate byte)
;
;       USES    AX, CX, SI, DI, Flags

DefProc CopySymbol, NEAR, LOCAL
        xor     ah,ah
        lodsb
        mov     cx,ax
        cmp     cx,MAX_NAME_SIZE - 1    ; check size, leave space for NULL
        jbe     csy1
        mov     cx,MAX_NAME_SIZE - 1
csy1:   rep     movsb
        mov     byte ptr [di],0
EndProc

;***    PrintPack - Prints a symbol on the debugging terminal.
;
;       ENTRY   (SI) = pointer to symbol packet to print.
;       EXIT    NONE
;       USES    SI, Flags

DefProc PrintPack, NEAR, LOCAL
        SaveReg <ax>
        xor     ah,ah
        Pcall   PrintPackName
ppkx:   RestReg <ax>
EndProc

;***    PrintPackLC - Prints a symbol on the debugging terminal.
;
;       ENTRY   (SI) = pointer to symbol packet to print.
;       EXIT    NONE
;       USES    SI, Flags

DefProc PrintPackLC, NEAR, LOCAL
        SaveReg <ax>
        mov     ah,1
        Pcall   PrintPackName
pplx:   RestReg <ax>
EndProc

;***    PrintPackName - Prints a symbol on the debugging terminal.
;
;       ENTRY   (AH) = lowercase flag
;               (SI) = pointer to symbol packet to print.
;       EXIT    NONE
;       USES    AX, SI, Flags

DefProc PrintPackName, NEAR, LOCAL
        SaveReg <cx>
ppn00:  lodsb                           ; Get length byte
        or      al,al                   ; Is it a null
        jz      short ppnx              ; Null so exit?
        xor     ch,ch                   ; Get length in cx
        mov     cl,al

;       Print all the characters of a symbol.
;       (CX) = length of string

ppn10:  lodsb                           ; Print the symbol
        or      ah,ah
        jz      short ppn20
        Pcall   lower
ppn20:  Pcall   putchar
        loop    ppn10
ppnx:   RestReg <cx>
EndProc

;***    SymErr
;
;       ENTRY:  (AX) = error code
;               (BX) = AddrS pointer
;               (DX) = if DEBUG defined, routine name
;
;       EXIT:   Doesn't return
;

DefProc SymErr, NEAR, LOCAL
IFDEF   DEBUG
        putstr  "Internal symbol error: "
        Pcall   rprbuf
;;      Pcall   DumpDebugStack
ELSE
        putstr  "Internal symbol error"
ENDIF
        Jump    ErrBreak
EndProc NoGen

CodeSegmentEnd

        end
