;       SCCSID = @(#)debmem.asm 6.1 90/11/17
PAGE    ,132
NAME    DEBMEM
TITLE   DEBMEM - debugger memory access routines
.286p

;***    DEBMEM.ASM - I286 kernel debugger additions
;
;       SCCSID = @(#)debmem.asm 13.19 90/05/23
;
;       This file contains much of the additions for the 286 kernel
;       debugger.
;
;
; Defects:
;
; 155531 RJM 23 May 1996 Make .D DPB work

.xlist
        include fnodeb.inc
        include basemaca.inc
        include osmaca.inc
        include osconst.inc
        include ostypes.inc
        include dossymc.inc
        include pgexport.inc
        include vmexport.inc
        include vmowner.inc
        include sem.inc
        include screen.inc
        include proc.inc
        include sigdef.inc
        include except.inc
        include infoseg.inc
        include curdir.inc
        include dirent.inc
        include find.inc
        include tkexport.inc
        include tkptda.inc
        include mvdmexp.inc
        include pglocal.inc
        include debseg.inc
        include debmac.inc
        include debequ.inc
        include debprt.inc
        include debdesc.inc

        include debinit.inc
.list

extrn  _printf:far
extrn  _GetAddress:far, _LinearToPhysical:far
extrn  _GetLinearAddress:far

REG_DSSI        equ     0
REG_ESDI        equ     1

DataSegmentStart


str1    db   "%04.4x - ",0
str2    db   "%02.2x - ",0

Extern  dumpInfoStruc, WORD
Extern  ptrSASvm, DWORD

Extern  DebData,        WORD
Extern  RegSA,          WORD
Extern  DefType,        WORD
Extern  LoadAllSav,     WORD
Extern  MemCache,       BYTE
Extern  tmpByte,        BYTE
Extern  tmpDword,       WORD
Extern  ptrGDT          WORD     ;  gdt, ldt, idt stored in df.cmd globals
Extern  sizeGDT         WORD
Extern  ptrLDT          WORD
Extern  sizeLDT         WORD
Extern  ptrIDT          WORD
Extern  sizeIDT         WORD

Extern  dumpInfoStruc    WORD

Global  LoadallSaved,   BYTE,   FALSE   ; loadall buffer was saved if TRUE
Static  DTRtemp,        FWORD,  0       ; temporary GDT/IDT, etc. storage

Static  ModeSwGdtAddr,  FWORD,  0       ; address of ModeswGDT

Static  ModeSwGdt,      QWORD,  0       ; GDT for 386 mode switch
Static  ModeSwGdtSpare, QWORD,  0       ; mode switch spare descriptor
MODESW_GDTSIZE  equ     ($ - dataOFFSET ModeSwGdt)
MODESW_SPARESEL equ     (dataOFFSET ModeSwGdtSpare - dataOFFSET ModeSwGdt)

Static  errmsg1, STRING, "Invalid selector"
Static  errmsg2, STRING, "Selector not present"
Static  errmsg3, STRING, "Past end of segment"
Static  errmsg4, STRING, "Invalid task number"
Static  errmsg5, STRING, "Invalid address type"
Static  errmsg6, STRING, "Page table not present"
Static  errmsg7, STRING, "Page not present"
Static  errmsg8, STRING, "Invalid linear address"
Static  errmsgx, STRING, "Invalid error code"

Static  errmsgtable, LABEL, WORD
        dw      dataOFFSET errmsgx
        dw      dataOFFSET errmsg1
        .errnz  DEBERR_INVALID_SELECTOR-1
        dw      dataOFFSET errmsg2
        .errnz  DEBERR_SELECTOR_NOT_PRESENT-2
        dw      dataOFFSET errmsg3
        .errnz  DEBERR_OFFSET_PAST_LIMIT-3
        dw      dataOFFSET errmsg4
        .errnz  DEBERR_INVALID_TASK_NUMBER-4
        dw      dataOFFSET errmsg5
        .errnz  DEBERR_INVALID_ADDR_TYPE-5
        dw      dataOFFSET errmsg6
        .errnz  DEBERR_PAGE_TABLE_NOT_PRESENT-6
        dw      dataOFFSET errmsg7
        .errnz  DEBERR_PAGE_NOT_PRESENT-7
        dw      dataOFFSET errmsg8
        .errnz  DEBERR_INVALID_ADDRESS-8
        dw      dataOFFSET errmsgx
        .errnz  DEBERR_CODE_MAX-8

extrn _ptrDIR:word
DataSegmentEnd

;**     SaveAddAddr
;
;       SaveAddAddr offaddr, trashreg
;
;       ENTRY:  (BX) = pointer to AddrS
;               (offaddr) = dword to add to AddrS
;
;       EXIT:   [bx].AddrOff on stack
;
;       USES:   trashreg, flags
;

SaveAddAddr     macro   offaddr, trashreg
        .errb   <offaddr>
        push    [bx].AddrOff.LowWord
        push    [bx].AddrOff.HighWord
        ifb     <trashreg>
                push    ax
                mov     ax,offaddr.LowWord
                add     [bx].AddrOff.LowWord,ax
                mov     ax,offaddr.HighWord
                adc     [bx].AddrOff.HighWord,ax
                pop     ax
        else
                mov     trashreg,offaddr.LowWord
                add     [bx].AddrOff.LowWord,trashreg
                mov     trashreg,offaddr.HighWord
                adc     [bx].AddrOff.HighWord,trashreg
        endif
        endm

;**     RestAddr
;
;       ENTRY:  (BX) = pointer to AddrS
;               [bx].AddrOff on stack
;
;       EXIT:   [bx].AddrOff restored
;
;       USES:   NONE
;

RestAddr        macro
        pop     [bx].AddrOff.HighWord
        pop     [bx].AddrOff.LowWord
        endm

CodeSegmentStart

;        extrn  _printf:far
;        extrn  _GetAddress:far, _LinearToPhysical:far
;        extrn  _GetLinearAddress:far
Extern  CmdBreak, NEAR

;***    Debug_Task - Entry for tasking operations
;
;       Only current tasking operation (fn=1) is to change BP task numbers.
;
;       ENTRY:  (AL) = fn # (TASK_CHANGE_TASKNUM = change BPs tasknumber)
;               (DI) = old task number
;               (DX) = new task number (TASK_DISABLE_BPS to disable BPs;
;                                       thread 1 dying)
;
;       EXIT:   NONE
;
;       USES:   NONE
;
        assumeDATA <ds, es, ss>

DefProc Debug_Task, FAR, PUBLIC
;        Pcall   EnterDebugRETF

;       If another function is added, must check value in AL

;        mov     di,RegSA.DIsav          ; restore parm reggies...
;        mov     dx,RegSA.DXsav          ; ...
;        Pcall   ChangeBPTaskNum         ; ...and call function
;        Jump    ExitDebug
EndProc

;***    GetRMIDTaddr - Get real mode interrrupt vector table address
;
;       Pcall GetRMIDTaddr
;
;       ENTRY:  (BX) = offset to address structure (AddrS)
;
;       EXIT:   NONE
;
;       USES:   Flags
;
        assumeDATA <ds, es, ss>

DefProc GetRMIDTaddr, NEAR, PUBLIC
        Pcall   FillPhyAddr, <bx, DebData.RealIDTaddr.DTRBaseLow, DebData.RealIDTaddr.DTRBaseHighExt>
EndProc

;***    GetPMIDTaddr - Get protected mode IDT address
;
;       Pcall GetPMIDTaddr
;
;       ENTRY:  (BX) = offset to address structure (AddrS)
;
;       EXIT    (CX) = size of idt (limit + 1)
;
;       USES    CX, FLAGS
;
        assumeDATA <ds, es, ss>

DefProc GetPMIDTaddr, NEAR, PUBLIC
        Pcall   FillLinAddr, <bx, DebData.ProtIDTaddr.DTRBaseLow, DebData.ProtIDTaddr.DTRBaseHighExt>
        mov     cx,DebData.ProtIDTaddr.DTRLimit
        inc     cx
EndProc

;***    GetGdtAddr - Get real mode interrrupt vector table address
;
;       Pcall GetGdtAddr
;
;       ENTRY:  (BX) = offset to address structure (AddrS)
;
;       EXIT:   (CX) = size (limit + 1)
;
;       USES:   CX, Flags
;
        assumeDATA <ds, es, ss>

DefProc GetGdtAddr, NEAR, PUBLIC
        Pcall   FillLinAddr, <bx, DebData.GDTaddr.DTRBaseLow, DebData.GDTaddr.DTRBaseHighExt>
        mov     cx,DebData.GDTaddr.DTRLimit
        inc     cx
EndProc

;***    GetLdtAddr - get pointer to ldt
;
;       Gets a read/write pointer to the ldt in any mode.
;
;       Pcall   GetLdtAddr
;
;       ENTRY   (BX) = pointer to AddrS structure to fill in
;
;       EXIT    (CX) = size of ldt (limit + 1)
;
;       USES    CX, FLAGS
;
        assumeDATA <ds, es, ss>

DefProc GetLdtAddr, NEAR, PUBLIC
        SaveReg <ax, dx, si, di>
        Pcall   FillSelAddr, <bx, RegSA.LDTsav, 0, 0>
        push    bx
        Pcall   GetAddrInfo                     ; (AX:DX) = linear address
        pop     bx                              ; (SI:DI) = limit
        jc      glp1
        Pcall   FillLinAddr, <bx, dx, ax>
        mov     cx,di                           ; (CX) = LDT limit
        inc     cx                              ; make limit a size
        clc
glp1:   RestReg <di, si, dx, ax>
EndProc

;***    GetCSIPaddr - Get CS:IP address
;
;       Pcall GetCSIPaddr
;
;       ENTRY:  (BX) = offset to address structure (AddrS)
;
;       EXIT:   NONE
;
;       USES:   Flags
;
        assumeDATA <ds, es, ss>

DefProc GetCSIPaddr, NEAR, PUBLIC
        Pcall   FillDefAddr, <bx, RegSA.CSsav, RegSA.EIPsav.LowWord, RegSA.EIPsav.HighWord>
EndProc

;***    GetDSaddr - Get Data Segment AddrS
;
;       Pcall GetDSaddr
;
;       ENTRY:  (BX) = offset to address structure (AddrS)
;
;       EXIT:   NONE
;
;       USES:   Flags
;
        assumeDATA <ds, es, ss>

DefProc GetDSaddr, NEAR, PUBLIC
        Pcall   FillDefAddr, <bx, RegSA.DSsav, 0, 0>
EndProc

;***    FillDataAddr - Fill AddrS structure with data
;
;       Pcall   FillDataAddr, <AddrPtr, FillData>
;
;       ENTRY:  (AddrPtr) = AddrS structure pointer
;               (FillData) = dword data
;
;       EXIT:   NONE
;
;       USES:   Flags
;
        assumeDATA <ds, es, ss>

DefProc FillDataAddr, NEAR, PUBLIC

ArgVar  AddrPtr, WORD
ArgVar  FillData, DWORD

BeginProc
        Pcall   FillAddr, <AddrPtr, 0, FillData.LowWord, FillData.HighWord, 0, EXPR_TYPE_DATA_W>
EndProc

;***    FillDefAddr - Fill AddrS structure with default address type
;
;       Pcall   FillDefAddr, <AddrPtr, FillSeg, FillOff>
;
;       ENTRY:  (AddrPtr) = AddrS structure pointer
;               (FillSeg) = selector of address
;               (FillOff) = dword offset of address
;
;       EXIT:   NONE
;
;       USES:   Flags
;
        assumeDATA <ds, es, ss>

DefProc FillDefAddr, NEAR, PUBLIC

ArgVar  AddrPtr, WORD
ArgVar  FillSeg, WORD
ArgVar  FillOff, DWORD

BeginProc
        Pcall   FillAddr, <AddrPtr, FillSeg, FillOff.LowWord, FillOff.HighWord, 0, DefType>
EndProc

;***    FillSelAddr - Fill AddrS structure with sel:off address
;
;       Pcall   FillSelAddr, <AddrPtr, FillSeg, FillOff>
;
;       ENTRY:  (AddrPtr) = AddrS structure pointer
;               (FillSeg) = selector of address
;               (FillOff) = dword offset of address
;
;       EXIT:   NONE
;
;       USES:   Flags
;
        assumeDATA <ds, es, ss>

DefProc FillSelAddr, NEAR, PUBLIC

ArgVar  AddrPtr, WORD
ArgVar  FillSeg, WORD
ArgVar  FillOff, DWORD

BeginProc
        Pcall   FillAddr, <AddrPtr, FillSeg, FillOff.LowWord, FillOff.HighWord, 0, EXPR_TYPE_SEL_W>
EndProc

;***    FillLinAddr - Fill AddrS structure with linear address
;
;       Pcall   FillLinAddr, <AddrPtr, Addr>
;
;       ENTRY:  (AddrPtr) = AddrS structure pointer
;               (Addr) = dword offset of address
;
;       EXIT:   NONE
;
;       USES:   Flags
;
        assumeDATA <ds, es, ss>

DefProc FillLinAddr, NEAR, PUBLIC

ArgVar  AddrPtr, WORD
ArgVar  Addr, DWORD

BeginProc
        Pcall   FillAddr, <AddrPtr, 0, Addr.LowWord, Addr.HighWord, 0, EXPR_TYPE_LIN_W>
EndProc

;***    FillPhyAddr - Fill AddrS structure with physical address
;
;       Pcall   FillPhyAddr, <AddrPtr, Addr>
;
;       ENTRY:  (AddrPtr) = AddrS structure pointer
;               (Addr) = dword offset of address
;
;       EXIT:   NONE
;
;       USES:   Flags
;
        assumeDATA <ds, es, ss>

DefProc FillPhyAddr, NEAR, PUBLIC

ArgVar  AddrPtr, WORD
ArgVar  Addr, DWORD

BeginProc
        Pcall   FillAddr, <AddrPtr, 0, Addr.LowWord, Addr.HighWord, 0, EXPR_TYPE_PHY_W>
EndProc

;***    FillAddr - Fill AddrS structure
;
;       Automaticly determines the AddrSize field if zero.
;
;       Pcall   FillAddr, <AddrPtr, FillSeg, FillOff, FillTask, FillType>
;
;       ENTRY:  (AddrPtr) = AddrS structure pointer
;               (FillSeg) = segment/selector of address
;               (FillOff) = dword offset of address
;               (FillTask) = address task number, 0 = current
;               (FillType) = address type
;
;       EXIT:   NONE
;
;       USES:   Flags
;
        assumeDATA <ds, es, ss>

DefProc FillAddr, NEAR, PUBLIC

ArgVar  AddrPtr, WORD
ArgVar  FillSeg, WORD
ArgVar  FillOff, DWORD
ArgVar  FillTask, WORD
ArgVar  FillType, WORD

BeginProc
        SaveReg <ax, di, es>
        push    ds     ; seg 10/05/93  df would trap while running under pmdf
        pop     es     ; seg 10/05/93
        mov     di,AddrPtr
        mov     ax,FillOff.LowWord
        stosw
        .errnz  AddrOff.LowWord - 0
        mov     ax,FillOff.HighWord
        stosw
        .errnz  AddrOff.HighWord - 2
        mov     ax,FillSeg
        stosw
        .errnz  AddrSeg - 4
        mov     ax,FillType
        or      ah,ah
        jnz     fla1                    ; jump if size is not zero
        mov     ah,EXPR_SIZE_DWORD
        cmp     FillOff.HighWord,0
        jne     fla1
        mov     ah,EXPR_SIZE_WORD
        cmp     FillOff.LowWord.HighByte,0
        jne     fla1
        mov     ah,EXPR_SIZE_BYTE
fla1:   stosw
        .errnz  AddrTypeSize - 6
        mov     ax,FillTask             ; (AX) = address task number
        or      ax,ax
        jnz     fla2
        Pcall   GetCurrTask             ; if zero, get current task number
fla2:   stosw
        .errnz  AddrTask - 8
        .errnz  (size AddrS) - 10
        RestReg <es, di, ax>
EndProc

;***    CompareAddr - compares two AddrS structures
;
;       Compares two addresses and returns if both point
;       at the physical piece of memory. Returns an error
;       if either are invalid.
;
;       Pcall CompareAddr, <Addr1, Addr2>
;
;       ENTRY:  (Addr1) = offset of first AddrS structure
;               (Addr2) = offset of second AddrS structure
;
;       EXIT:   if carry clear,
;                       (AX) = 1   for Addr1 > Addr2 (ZF=0, SF=0)
;                       (AX) = -1  for Addr1 < Addr2 (ZF=0, SF=1)
;                       (AX) = 0   for Addr1 = Addr2 (ZF=1, SF=0)
;               if carry set,
;                       (AX) = error code
;                       (BX) = AddrS pointer in error
;
;
;       USES:   AX, BX, Flags
;
        assumeDATA <ds, es, ss>

DefProc CompareAddr, NEAR, PUBLIC

ArgVar  Addr1,  WORD
ArgVar  Addr2,  WORD

BeginProc
        SaveReg <cx, dx>
        mov     bx,Addr1
        mov     cx,1
        Pcall   AddrToPhy       ; (AX:DX) = physical address for Addr1
        jc      cpgx            ; jump to exit, clean up stack

        mov     bx,Addr2
        mov     Addr1,dx
        mov     Addr2,ax        ; (Addr2:Addr1) = phys addr1
        mov     cx,1
        Pcall   AddrToPhy       ; (AX:DX) = physical address for Addr2
        jc      cpgx            ; jump to exit, clean up stack

        Pcall   CompareLong, <dx, ax, Addr1, Addr2>
        mov     ax,-1           ; (AX) = -1
        ja      cpg1            ; jump if Long1 > Long2
        jb      cpg2            ; jump if Long1 < Long2
        inc     ax              ; (AX) = 0
cpg1:   neg     ax              ; (AX) = -(AX)
cpg2:   or      ax,ax           ; setup flags and clear carry
cpgx:   RestReg <dx, cx>
EndProc

;***    CompareLinAddr - compares two AddrS structures *linear* addresses
;
;       Convert's addresses to linear addresses and compares them.
;
;       Pcall CompareLinAddr, <Addr1, Addr2>
;
;       ENTRY:  (Addr1) = offset of first AddrS structure
;               (Addr2) = offset of second AddrS structure
;
;       EXIT:   if carry clear,
;                       (AX) = 1   for Addr1 > Addr2 (ZF=0, SF=0)
;                       (AX) = -1  for Addr1 < Addr2 (ZF=0, SF=1)
;                       (AX) = 0   for Addr1 = Addr2 (ZF=1, SF=0)
;               if carry set,
;                       (AX) = error code
;                       (BX) = AddrS pointer in error
;
;
;       USES:   AX, BX, Flags
;
        assumeDATA <ds, es, ss>

DefProc CompareLinAddr, NEAR, PUBLIC

ArgVar  Addr1,  WORD
ArgVar  Addr2,  WORD

BeginProc
        SaveReg <cx, dx>
        mov     bx,Addr1
        mov     cx,1
        Pcall   AddrToLin       ; (AX:DX) = linear address for Addr1
        jc      cplx            ; jump to exit, clean up stack

        mov     bx,Addr2
        mov     Addr1,dx
        mov     Addr2,ax        ; (Addr2:Addr1) = linear addr1
        mov     cx,1
        Pcall   AddrToLin       ; (AX:DX) = linear address for Addr2
        jc      cplx            ; jump to exit, clean up stack

        Pcall   CompareLong, <dx, ax, Addr1, Addr2>
        mov     ax,1            ; (AX) = 1
        ja      cpl1            ; jump if Long1 > Long2
        jb      cpl2            ; jump if Long1 < Long2
        dec     ax              ; (AX) = 0
cpl1:   neg     ax              ; (AX) = -(AX)
cpl2:   or      ax,ax           ; setup flags and clear carry
cplx:   RestReg <dx, cx>
EndProc

;***    CompareLong - unsigned compare of two longs.
;
;       Pcall   CompareLong, <Long1, Long2>
;
;       ENTRY:  (Long1) = first 32 bit long.
;               (Long2) = second 32 bit long.
;
;       EXIT:   Long1 > Long2 (CF=0, ZF=0)
;               Long1 < Long2 (CF=1, ZF=0)
;               Long1 = Long2 (CF=0, ZF=1)
;
;       USES:   Flags
;
        assumeDATA <ds, es, ss>

DefProc CompareLong, NEAR, LOCAL

ArgVar  Long1, DWORD
ArgVar  Long2, DWORD

BeginProc
        push    ax
        .386p
        mov     eax,Long1
        cmp     eax,Long2
        .286p
        pop     ax
EndProc

;***    CopyAddr
;
;       Copys one AddrS structure to another.
;
;       Pcall   CopyAddr, <Addr1, Addr2>
;
;       ENTRY:  (Addr1) = source AddrS structure pointer
;               (Addr2) = destination AddrS structure pointer
;
;       EXIT:   NONE
;
;       USES:   FLAGS
;
        assumeDATA <ds, es, ss>

DefProc CopyAddr, NEAR, PUBLIC

ArgVar  Addr1, WORD
ArgVar  Addr2, WORD

BeginProc
        SaveReg <cx, si, di>
        mov     si,Addr1
        mov     di,Addr2
        mov     cx,(size AddrS)/2
        .errnz  (size AddrS) mod 2
        rep movsw
        RestReg <di, si, cx>
EndProc

;***    AddAddr
;
;       Adds a value correctly to an address structure.
;
;       Pcall   AddAddr, <Value.LowWord, Value.HighWord>
;
;       ENTRY:  (Value) = value to add to address.
;               (BX) = pointer to AddrS structure
;
;       EXIT:   Carry set
;                   Unsigned addition overflowed 32 bits
;               Carry clear
;                   Unsigned addition did not overflow 32 bits
;
;       USES:   FLAGS
;
        assumeDATA <ds, es, ss>

DefProc AddAddr, NEAR, PUBLIC

ArgVar  Value, DWORD

BeginProc
        SaveReg <ax, dx>
        mov     dx,Value.LowWord
        mov     ax,Value.HighWord
        add     [bx].AddrOff.LowWord,dx         ; add with carry to high byte
        adc     [bx].AddrOff.HighWord,ax        ;
        RestReg <dx, ax>
EndProc

;***    SubtractAddr
;
;       Subtract first address from second after converting to physical
;       addresses.
;
;       Pcall   SubtractAddr, <Addr1, Addr2>
;
;       ENTRY:  (Addr1) = first AddrS pointer.
;               (Addr2) = second AddrS pointer.
;
;       EXIT:   if carry clear,
;                       (AX:DX) = Addr1 - Addr2.
;               if carry set,
;                       (BX) = pointer to bad AddrS
;                       (AX) = error code
;                       or
;                       (AX) = 0 if Addr2 > Addr1.
;
;       USES:   AX, BX, DX, FLAGS
;
        assumeDATA <ds, es, ss>

DefProc SubtractAddr, NEAR, PUBLIC

ArgVar  Addr1, WORD
ArgVar  Addr2, WORD
LocVar  BaseAddr, DWORD

BeginProc
        SaveReg <cx>
        mov     bx,[Addr2]
        Pcall   GetAddrType             ; (AL) = address type
        cmp     al,EXPR_TYPE_PHY
        jne     short sta10             ; jump if not a physical address

        mov     ax,[bx].AddrOff.HighWord
        mov     dx,[bx].AddrOff.LowWord ; (AX:DX) = phys addr2
        mov     [BaseAddr].HighWord,ax  ; (BaseAddr) = phys addr2
        mov     [BaseAddr].LowWord,dx

        mov     bx,[Addr1]
        Pcall   GetAddrType             ; (AL) = address type
        cmp     al,EXPR_TYPE_PHY
        mov     ax,[bx].AddrOff.HighWord
        mov     dx,[bx].AddrOff.LowWord ; (AX:DX) = phys addr1
        je      short sta20             ; jump if both are physical addresses
        mov     ax,DEBERR_INVALID_ADDR_TYPE
        stc
        jmp     short stax

sta10:  mov     cx,1
        Pcall   AddrToLin               ; (AX:DX) = linear addr2
        jc      short stax
        mov     [BaseAddr].HighWord,ax  ; (BaseAddr) = linear addr2
        mov     [BaseAddr].LowWord,dx

        mov     bx,[Addr1]
        mov     cx,1
        Pcall   AddrToLin               ; (AX:DX) = linear addr1
        jc      short stax

sta20:  sub     dx,[BaseAddr].LowWord
        sbb     ax,[BaseAddr].HighWord
        jnc     short stax
        xor     ax,ax                   ; (AX) = 0 when Addr2 > Addr1
        stc
stax:   RestReg <cx>
EndProc

;***    CheckAddr
;
;       This routine verifies that an selector is valid.
;
;       Pcall   CheckAddr, <AddrPtr>
;
;       ENTRY:  (AddrPtr) = pointer to AddrS struc
;
;       EXIT:   NONE
;
;       USES:   FLAGS
;
        assumeDATA <ds, es, ss>

DefProc CheckAddr, NEAR, PUBLIC

ArgVar  AddrPtr, WORD

BeginProc
        SaveReg <ax, bx, cx, dx>
        mov     bx,AddrPtr
        mov     cx,1
        Pcall   AddrToPhy
        jnc     cadx
        Pcall   PrintMemErr         ; display the error message
        Pcall   crlf
        jmp     CmdBreak            ; quit this command and go to cmd line
cadx:   RestReg <dx, cx, bx, ax>
EndProc

;***    geterrmessage - returns a pointer to memory error message
;
;       Ccall   geterrmessage(err)
;
;       ENTRY:  (err) = error code
;
;       EXIT:   (str) = pointer to string
;
;       USES:   AX, DX, BX, Flags
;
        assumeDATA <ds, es, ss>

DefProc geterrmessage, FAR, PUBLIC, C

ArgVar  gem_err,WORD

BeginProc
        mov     bx,[gem_err]
        cmp     bx,DEBERR_CODE_MAX
        jbe     short gem10
        mov     bx,DEBERR_CODE_MAX+1
gem10:  shl     bx,1
        mov     ax,errmsgtable[bx]
        mov     dx,ds               ; return selector of string
EndProc

;***    PrintMemErr - Prints the memory error passed
;
;       Pcall   PrintMemErr
;
;       ENTRY:  (AX) = error code from PutMemByteErr
;               (BX) = pointer to bad AddrS struc
;
;       EXIT:   NONE
;
;       USES:   SI, Flags
;
        assumeDATA <ds, es, ss>

DefProc PrintMemErr,NEAR,PUBLIC
        DataContext <ds,es>
        Pcall   CrLfColOne
        mov     si,ax
        push    bx
        push    ax
        call    _geterrmessage
        add     sp,2
;        Ccall   geterrmessage, <ax>
        pop     bx
        mov     dx,ax       ; get the string pointer in DX
        Pcall   rprbuf      ; print the error string
        mov     ax,':'
        Pcall   put_ch      ; and a colon
        mov     al,' '
        Pcall   put_ch      ; and a blank

;        printf  <"%s: ">, <ax>

        cmp     si,DEBERR_INVALID_TASK_NUMBER
        jne     short prm10
        push    bx

        push    [bx].AddrTask
        push    ds
        push    offset str1
        call    _printf
        add     sp,6

        pop     bx
;        printf  <"%04.4x - ">, <[bx].AddrTask>

prm10:  cmp     si,DEBERR_INVALID_ADDR_TYPE
        jne     short prm20
        push    bx

        push    [bx].AddrTypeSize
        push    ds
        push    offset str2
        call    _printf
        add     sp,6

        pop     bx
;        printf  <"%02.2x - ">, <[bx].AddrTypeSize>

prm20:  or      si,si
        jz      short prm30
        cmp     si,DEBERR_CODE_MAX
        jbe     short prm40
prm30:  nop
        push    bx

        push    si
        push    ds
        push    offset str1
        call    _printf
        add     sp,6

        pop     bx
;       printf  <"%04.4x - ">, <si>
prm40:  Pcall   OutAddrS, <bx>
EndProc

;***    PutByteOff - put a byte into memory
;
;       Pcall   PutByteOff, <OffAddr.LowWord, OffAddr.HighWord>
;
;       ENTRY:  (AL) = byte to put into memory
;               (BX) = offset to address structure (AddrS)
;               (OffAddr) = offset of word from address
;
;       EXIT:   if carry clear,
;                       memory written
;               if carry set,
;                       (AX) = error code
;
;       USES:   AX, Flags
;
        assumeDATA <ds, es, ss>

DefProc PutByteOff, NEAR, PUBLIC

ArgVar  OffAddr, DWORD

BeginProc
        SaveAddAddr OffAddr
        Pcall   PutByte
        RestAddr
EndProc

;***    PutByte - put a byte into memory
;
;       Pcall   PutByte
;
;       ENTRY:  (AL) = byte to put into memory
;               (BX) = offset to address structure (AddrS)
;
;       EXIT:   if carry clear,
;                       memory written
;               if carry set,
;                       (AX) = error code
;
;       USES:   AX, Flags
;
        assumeDATA <ds, es, ss>

DefProc PutByte, NEAR, PUBLIC
        SaveReg <cx, di>
        push    ax                      ; save byte to be written
        mov     ax,REG_ESDI
        mov     cx,1
        Pcall   MakeAddrPtr
    ASSUME   ES:NOTHING
        pop     cx                      ; (CL) = byte to be written
        jc      pbtx
        mov     es:[di],cl              ; CARRY CLEAR by MakeAddrPtr
pbtx:   DataContext <es>,di
        RestReg <di, cx>
EndProc

;***    PutWord - put a word into memory
;
;       Pcall   PutWord
;
;       ENTRY:  (BX) = offset to address structure (AddrS)
;               (DX) = word to put into memory
;
;       EXIT:   if carry clear,
;                       memory written
;               if carry set,
;                       (AX) = error code
;
;       USES:   AX, Flags
;
        assumeDATA <ds, es, ss>

DefProc PutWord, NEAR, PUBLIC
        xor     ax,ax
        Pcall   PutWordOff, <ax, ax>
EndProc

;***    PutWordOff - put a word into memory
;
;       Pcall   PutWordOff, <OffAddr.LowWord, OffAddr.HighWord>
;
;       ENTRY:  (BX) = pointer to address structure (AddrS)
;               (DX) = word to put into memory
;               (OffAddr) = offset of word from address
;
;       EXIT:   if carry clear,
;                       memory written
;               if carry set,
;                       (AX) = error code
;
;       USES:   AX, Flags
;
        assumeDATA <ds, es, ss>

DefProc PutWordOff, NEAR, PUBLIC

ArgVar  OffAddr, DWORD
LocVar  WordBuf, WORD

BeginProc
        SaveReg <cx, si>
        mov     cx,2
        lea     si,WordBuf
        mov     WordBuf,dx
        Pcall   PutBuffOff, <OffAddr.LowWord, OffAddr.HighWord>
        RestReg <si, cx>
EndProc

;***    PutDword - put a dword into memory
;
;       Pcall   PutDword
;
;       ENTRY:  (AX:DX) = dword to put into memory
;               (BX) = offset to address structure (AddrS)
;
;       EXIT:   if carry clear,
;                       memory written
;               if carry set,
;                       (AX) = error code
;
;       USES:   AX, Flags
;
        assumeDATA <ds, es, ss>

DefProc PutDword, NEAR, PUBLIC
        Pcall   PutDwordOff, <0, 0>
EndProc

;***    PutDwordOff - put a dword into memory
;
;       Pcall   PutDwordOff, <OffAddr.LowWord, OffAddr.HighWord>
;
;       ENTRY:  (AX:DX) = dword to put into memory
;               (BX) = pointer to address structure (AddrS)
;               (OffAddr) = offset of word from address
;
;       EXIT:   if carry clear,
;                       memory written
;               if carry set,
;                       (AX) = error code
;
;       USES:   AX, Flags
;
        assumeDATA <ds, es, ss>

DefProc PutDwordOff, NEAR, PUBLIC

ArgVar  OffAddr, DWORD
LocVar  DwordBuf, DWORD

BeginProc
        SaveReg <cx, si>
        mov     cx,4
        mov     DwordBuf.HighWord,ax
        mov     DwordBuf.LowWord,dx
        lea     si,DwordBuf
        Pcall   PutBuffOff, <OffAddr.LowWord, OffAddr.HighWord>
        RestReg <si, cx>
EndProc

;***    PutBuff - put a buffer into memory
;
;       Pcall   PutBuff
;
;       ENTRY:  (BX) = offset to address structure (AddrS)
;               (CX) = number of bytes to put
;               (SI) = offset to buffer to put memory
;
;       EXIT:   if carry clear,
;                       memory written
;               if carry set,
;                       (AX) = error code
;
;       USES:   AX, Flags
;
        assumeDATA <ds, es, ss>

DefProc PutBuff, NEAR, PUBLIC
        xor     ax,ax
        Pcall   PutBuffOff, <ax, ax>
EndProc

;***    PutBuffOff - put a buffer into memory
;
;       Pcall   PutBuffOff, <OffAddr.LowWord, OffAddr.HighWord>
;
;       ENTRY:  (BX) = offset to address structure (AddrS)
;               (CX) = number of bytes to put
;               (SI) = offset to buffer
;               (OffAddr) = offset from above AddrS
;
;       EXIT:   if carry clear,
;                       memory written
;               if carry set,
;                       (AX) = error code
;
;       USES:   AX, Flags
;
        assumeDATA <ds, es, ss>

DefProc PutBuffOff, NEAR, PUBLIC

ArgVar  OffAddr, DWORD
LocVar  TmpAddr, <size AddrS>

BeginProc
        SaveReg <cx, dx, si, di>
        SaveAddAddr OffAddr, ax
        jcxz    short pbox
        mov     dx,cx                   ; (DX) = number of bytes to copy

pbo1:   mov     ax,REG_ESDI
        Pcall   MakeAddrPtr             ; (ES:DI) = pointer to destination
    ASSUME   ES:NOTHING
        jc      pbox

        push    cx
        Pcall   DebCopy
        pop     cx                      ; (CX) = number of bytes copied

        DataContext <es>,di
        Pcall   AddAddr, <cx, 0>        ; point address after the bytes copied

        sub     dx,cx                   ; (DX) = number of bytes left
        mov     cx,dx                   ; (CX) = number of bytes left
        jnz     pbo1

;       CARRY CLEAR by sub dx,cx

pbox:   DataContext <es>,di
        RestAddr
        RestReg <di, si, dx, cx>
EndProc

;***    GetCache - get memory block buffered in a cache
;
;       Pcall   GetCache
;
;       ENTRY:  (BX) = pointer to AddrS
;               (DX) = number of bytes
;
;       EXIT:   if carry clear,
;                       (CX) = number of bytes in cache
;                       (DX) = number of bytes left to get  (0 if none left)
;                       (DI) = pointer to cached bytes
;               if carry set,
;                       (AX) = error code
;
;       USES:   AX, CX, DX, DI, Flags
;
;       NOTE:   GetCache ignores the selector limit and gets the
;               number of bytes requested anyway.
;
        assumeDATA <ds, es, ss>

DefProc GetCache, NEAR, PUBLIC
        xor     ax,ax
        Pcall   GetCacheOff, <ax, ax>
EndProc

;***    GetCacheOff - get memory block buffered in a cache
;
;       Pcall   GetCacheOff, <OffAddr.LowWord, OffAddr.HighWord>
;
;       ENTRY:  (BX) = pointer to AddrS
;               (DX) = number of bytes
;               (OffAddr) = offset from AddrS
;
;       EXIT:   if carry clear,
;                       (CX) = number of bytes in cache
;                       (DX) = number of bytes left to get (0 if none left)
;                       (DI) = pointer to cached bytes
;               if carry set,
;                       (AX) = error code
;
;       USES:   AX, CX, DX, DI, Flags
;
;       NOTE:   GetCacheOff ignores the selector limit and gets the
;               number of bytes requested anyway.
;
        assumeDATA <ds, es, ss>

DefProc GetCacheOff, NEAR, PUBLIC

ArgVar  OffAddr, DWORD
LocVar  TmpAddr, <size AddrS>

BeginProc
        mov     cx,dx                   ; (CX) = number of bytes to get
        jcxz    gco2
        cmp     cx,MEMCACHESIZE
        jbe     short gco1
        mov     cx,MEMCACHESIZE
gco1:   mov     di,dataOFFSET MemCache  ; (DI) = pointer to memory cache
        Pcall   GetMemOff, <OffAddr.LowWord, OffAddr.HighWord>
        jc      gcox
gco2:   sub     dx,cx                   ; (DX) = number of bytes left
        clc
gcox:
EndProc

;***    GetMem - get memory block until selector limit or invalid page
;
;       GetMem reads as many bytes as possible until the selector
;       limit or an invalid page is read.  GetBuff differs in that
;       an error is returned if any of the bytes requested are not
;       with in the limit or on an invalid page.
;
;       Pcall   GetMem
;
;       ENTRY:  (BX) = offset to address structure (AddrS)
;               (CX) = number of bytes to get
;               (DI) = offset to buffer
;
;       EXIT:   if carry clear,
;                       (CX) = number of bytes read from memory
;               if carry set,
;                       (AX) = error code
;
;       USES:   AX, CX, Flags
;
        assumeDATA <ds, es, ss>

DefProc GetMem, NEAR, PUBLIC
        SaveReg <bx, cx, dx, si, di>
        push    cx
        push    di
;155531 we don't need to do a limit check here since GetLinearAddress
;155531 it. Furthermore some callers wan't partial data regardless of
;155531 of length in cx
        xor     cx,cx                   ;155531 cx=no limit/length check
        Pcall   AddrToLin               ; convert to a linear address
        pop     di
        pop     cx
        jc      @F                      ; leave if error

        push    0               ; don't build control blocks
        push    ds
        push    di                      ; DS:DI = buffer pointer
        push    0                       ; length hi word = 0
        push    cx                      ; number of bytes to get
        push    ax
        push    dx                      ; linear address
        call    _GetLinearAddress       ; read this data
        add     sp,14                   ; clean up parameters
        cmp     ax,0                    ; error?
        stc
        jnz     @F
        clc
@@:
        DataContext <es>,di
        RestReg <di, si, dx, cx, bx>
EndProc

;***    GetMemOff - get memory block until selector limit or invalid page
;
;       GetMemOff reads as many bytes as possible until the selector
;       limit or an invalid page is read.  GetBuffOff differs in that
;       an error is returned if any of the bytes requested are not
;       with in the limit or on an invalid page.
;
;       Pcall   GetMemOff, <OffAddr.LowWord, OffAddr.HighWord>
;
;       ENTRY:  (BX) = offset to address structure (AddrS)
;               (CX) = number of bytes to get
;               (DI) = offset to buffer
;               (OffAddr) = offset from AddrS
;
;       EXIT:   if carry clear,
;                       (CX) = number of bytes read from memory
;               if carry set,
;                       (AX) = error code
;
;       USES:   AX, CX, Flags
;
        assumeDATA <ds, es, ss>

DefProc GetMemOff, NEAR, PUBLIC

ArgVar  OffAddr, DWORD

BeginProc
        SaveAddAddr OffAddr, ax
        Pcall   GetMem
        RestAddr
EndProc

;***    GetByteOff - get a byte from memory
;
;       Pcall   GetByteOff, <OffAddr.LowWord, OffAddr.HighWord>
;
;       ENTRY:  (BX) = offset to address structure (AddrS)
;               (OffAddr) = offset of word from address
;
;       EXIT:   if carry clear,
;                       (AL) = byte from memory
;               if carry set,
;                       (AX) = error code
;
;       USES:   AX, Flags
;
        assumeDATA <ds, es, ss>

DefProc GetByteOff, NEAR, PUBLIC

ArgVar  OffAddr, DWORD

BeginProc
        SaveAddAddr OffAddr, ax
        Pcall   GetByte
        RestAddr
EndProc

;***    GetByte - get a byte from memory
;
;       Pcall   GetByte
;
;       ENTRY:  (BX) = offset to address structure (AddrS)
;
;       EXIT:   if carry clear,
;                       (AL) = byte from memory
;               if carry set,
;                       (AX) = error code
;
;       USES:   AX, Flags
;
        assumeDATA <ds, es, ss>

DefProc GetByte, NEAR, PUBLIC
        SaveReg <bx, cx, dx, si, di>
        mov     cx,1                    ; length of region
        Pcall   AddrToPhy               ; convert any address to physical
        jc      @F                      ; if error, don't read it
        push    ds
        push    offset tmpByte          ; junk byte
        push    1                       ; number of bytes to get
        push    ax
        push    dx                      ; linear address
        call    _GetAddress             ; read this data
        add     sp,10                   ; clean up parameters
        cmp     ax,0                    ; error?
        stc
        jnz     @F
        mov     al,tmpByte              ; (AL) = byte requested, CARRY CLEAR
        clc
@@:     DataContext <es>,di
        RestReg <di, si, dx, cx, bx>
EndProc

;***    GetWord - get a word from memory
;
;       Pcall   GetWord
;
;       ENTRY:  (BX) = offset to address structure (AddrS)
;
;       EXIT:   if carry clear,
;                       (DX) = word from memory
;               if carry set,
;                       (AX) = error code
;
;       USES:   AX, DX, Flags
;
        assumeDATA <ds, es, ss>

DefProc GetWord, NEAR, PUBLIC
        SaveReg <bx, cx, si, di>
        mov     cx,2                ; length of region = 2
        Pcall   AddrToPhy           ; convert to a physical address
        jc      @F                  ; leave if error

        push    ds
        push    offset tmpDword         ; junk word
        push    2                       ; number of bytes to get
        push    ax
        push    dx                      ; physical address
        call    _GetAddress             ; read this data
        add     sp,10                   ; clean up parameters
        cmp     ax,0                    ; error?
        stc
        jnz     @F
        lea     di,tmpDword
        mov     dx,[di]                 ; (DX) = word requested, CARRY CLEAR
        clc
@@:     DataContext <es>,di
        RestReg <di, si, cx, bx>
EndProc

;***    GetWordOff - get a word from memory
;
;       Pcall   GetWordOff, <OffAddr.LowWord, OffAddr.HighWord>
;
;       ENTRY:  (BX) = pointer to address structure (AddrS)
;               (OffAddr) = offset of word from address
;
;       EXIT:   if carry clear,
;                       (DX) = word from memory
;               if carry set,
;                       (AX) = error code
;
;       USES:   AX, DX, Flags
;
        assumeDATA <ds, es, ss>

DefProc GetWordOff, NEAR, PUBLIC

ArgVar  OffAddr, DWORD

BeginProc
        SaveAddAddr OffAddr, ax
        Pcall   GetWord
        RestAddr
EndProc

;***    GetDword - get a dword from memory
;
;       Pcall   GetDword
;
;       ENTRY:  (BX) = offset to address structure (AddrS)
;
;       EXIT:   if carry clear,
;                       (AX:DX) = dword from memory
;               if carry set,
;                       (AX) = error code
;
;       USES:   AX, DX, Flags
;
        assumeDATA <ds, es, ss>

DefProc GetDword, NEAR, PUBLIC
        SaveReg <bx, cx, si, di>
        mov     cx,4                    ; length of region to validate
        Pcall   AddrToPhy               ; convert to a physical address
        jc      @F                      ; leave if error

        push    ds
        push    offset tmpDword         ; junk word
        push    4                       ; number of bytes to get
        push    ax
        push    dx                      ; physical address
        call    _GetAddress             ; read this data
        add     sp,10                   ; clean up parameters
        cmp     ax,0                    ; error?
        stc
        jnz     @F
        lea     di,tmpDword
        mov     dx,[di]             ; (AX:DX) = dword requested, CARRY CLEAR
        mov     ax,2[di]
        clc
@@:     DataContext <es>,di
        RestReg <di, si, cx, bx>
EndProc

;***    GetDwordOff - get a dword from memory
;
;       Pcall   GetDwordOff, <OffAddr.LowWord, OffAddr.HighWord>
;
;       ENTRY:  (BX) = pointer to address structure (AddrS)
;               (OffAddr) = offset of word from address
;
;       EXIT:   if carry clear,
;                       (AX:DX) = dword from memory
;               if carry set,
;                       (AX) = error code
;
;       USES:   AX, DX, Flags
;
        assumeDATA <ds, es, ss>

DefProc GetDwordOff, NEAR, PUBLIC

ArgVar  OffAddr, DWORD

BeginProc
        SaveAddAddr OffAddr, ax
        Pcall   GetDword
        RestAddr
EndProc

;***    GetBuff - get a buffer from memory
;
;       Pcall   GetBuff
;
;       ENTRY:  (BX) = offset to address structure (AddrS)
;               (CX) = number of bytes to get
;               (DI) = offset to buffer
;
;       EXIT:   if carry clear,
;                       memory read
;               if carry set,
;                       (AX) = error code
;
;       USES:   AX, Flags
;
        assumeDATA <ds, es, ss>

DefProc GetBuff, NEAR, PUBLIC
        Pcall   GetMem
EndProc

;***    GetBuffOff - get a buffer from memory
;
;       Pcall   GetBuffOff, <OffAddr>
;
;       ENTRY:  (BX) = offset to address structure (AddrS)
;               (CX) = number of bytes to get
;               (DI) = offset to buffer
;               (OffAddr) = offset from above AddrS
;
;       EXIT:   if carry clear,
;                       memory read
;               if carry set,
;                       (AX) = error code
;
;       USES:   AX, Flags
;
        assumeDATA <ds, es, ss>

DefProc GetBuffOff, NEAR, PUBLIC

ArgVar  OffAddr, DWORD

BeginProc
        SaveAddAddr OffAddr, ax
        Pcall   GetMem
        RestAddr
EndProc

;***    DebCopy - copy memory fast
;
;       ENTRY:  (DS:SI) = pointer to source
;               (ES:DI) = pointer to destination
;               (CX)    = number of bytes
;
;       EXIT:   NONE
;
;       USES:   CX, SI, DI, Flags
;
        ASSUME  DS:NOTHING, ES:NOTHING
        assumeDATA <ss>

DefProc DebCopy, NEAR, LOCAL
        jcxz    short cpy2              ; zero count, do nothing
        test    si,1
        jz      short cpy1              ; jump if source is even
        test    di,1
        jz      short cpy1              ; jump if dest is even
        movsb                           ; copy one byte to make both even
        dec     cx                      ; adjust the count
        jz      cpy2                    ; it was only one byte
cpy1:   shr     cx,1                    ; (CX) = word count, CY if count odd
        rep movsw                       ; mov CX words (doesn't jam carry flag)
        adc     cx,cx                   ; if count was even, CX == 0
        rep movsb                       ; copy last uneven byte
cpy2:
EndProc

;***    MakeAddrPtr - sets up a pointer to an address
;
;       Pcall   MakeAddrPtr
;
;       ENTRY:  (AX) = register set flag
;                       REG_DSSI
;                       REG_ESDI
;               (BX) = pointer to AddrS struc
;               (CX) = number of bytes, 0 == no limit check
;
;       EXIT:   if carry set,
;                       (AX) = error code
;               if carry clear,
;                       (CX) = number of valid bytes, 0 == 64K
;                       if (SI) == REG_DSSI then
;                               (DS:SI) = pointer
;                       if (SI) == REG_ESDI then
;                               (ES:DI) = pointer
;       USES:   AX, CX, Flags
;               if Flag = REG_DSSI uses ES, DI
;               if Flag = REG_DSSI uses DS, SI
;
;       NOTE:   The segment register maybe set up using the loadall
;               instruction in real mode to access memory above 1
;               meg. The segment register can not be saved and
;               restored because when the register is reload it will
;               undo the effect of the loadall.
;
        assumeDATA <ds, es, ss>

DefProc MakeAddrPtr, NEAR, LOCAL
        SaveReg <bx, dx>
        push    ax
        Pcall   AddrToPhy               ; (AX:DX) = physical address
        pop     bx                      ; (BX) = register flag
        jc      mptx
        push    cx
        mov     cx,bx                   ; (CX) = register flag
        Pcall   PhyToPtr
        pop     cx                      ; (CX) = number of valid bytes
        clc
mptx:   RestReg <dx, bx>
EndProc

;***    AddrToPhy - Gets an physical address for any address type
;
;       Pcall   AddrToPhy
;
;       ENTRY:  (BX) = pointer to AddrS struc
;               (CX) = length of region, 0 == no limit check
;
;       EXIT:   If carry clear,
;                       (AX:DX) = physical address
;                       (CX) = valid length of physical region, 0 == 64K
;               If carry set,
;                       (AX) = error code
;
;       USES:   AX, CX, DX, FLAGS
;
        assumeDATA <ds, es, ss>

DefProc AddrToPhy, NEAR, PUBLIC
        Pcall   GetAddrType             ; (AL) = address type
        .errnz  EXPR_TYPE_DATA
        or      al,al
        jz      atp1
        cmp     al,EXPR_TYPE_PHY
        je      atp1
        Pcall   AddrToLin
        jc      atpx
        push    bx
        mov     bx,[bx].AddrTask        ; (BX) = task number
        Pcall   LinToPhy
        pop     bx
        jmp     short atpx

atp1:   mov     ax,[bx].AddrOff.HighWord
        mov     dx,[bx].AddrOff.LowWord ; (AX:DX) = physical address
        clc
atpx:
EndProc

;***    AddrToLin - Gets an linear address for any address type
;
;       Pcall   AddrToLin
;
;       ENTRY:  (BX) = pointer to AddrS struc
;               (CX) = length of region, 0 == no limit check
;
;       EXIT:   If carry clear,
;                       (AX:DX) = linear address.
;                       (CX) = valid length of linear region, 0 == 64K
;               If carry set,
;                       (AX) = error code
;
;       USES:   AX, CX, DX, FLAGS
;
        assumeDATA <ds, es, ss>

DefProc AddrToLin, NEAR, PUBLIC
        Pcall   GetAddrType             ; (AL) = address type
        .errnz  EXPR_TYPE_DATA
        or      al,al
        jz      atl1
        cmp     al,EXPR_TYPE_LIN
        je      atl1
        cmp     al,EXPR_TYPE_SEG
        je      atl2
        cmp     al,EXPR_TYPE_SEL
        je      atl3

;       Can not handle physical addresses

        mov     ax,DEBERR_INVALID_ADDR_TYPE
        stc
        jmp     short atlx

atl1:   mov     ax,[bx].AddrOff.HighWord
        mov     dx,[bx].AddrOff.LowWord ; (AX:DX) = linear address
        clc
        jmp     short atlx

atl2:   Pcall   SegOffToLin
        jmp     short atlx

atl3:   Pcall   SelOffToLin
atlx:
EndProc

;***    SegOffToLin - segment:offset to linear address
;
;       Convert a segment:offset to a linear address.
;
;       Pcall   SegOffToLin
;
;       ENTRY:  (BX) = pointer to AddrS struc
;               (CX) = length of region, 0 == no limit check
;
;       EXIT:   Carry clear, no error.
;                       (AX:DX) = linear address.
;                       (CX) = valid length of linear region, 0 == 64K
;
;       USES:   AX, CX, DX, FLAGS
;
        assumeDATA <ds, es, ss>

DefProc SegOffToLin, NEAR, LOCAL
        xor     ax,ax
        mov     dx,[bx].AddrSeg         ; (DX) = segment
        mov     al,dh                   ; (AL) = high byte of segment
        shl     dx,4                    ; (DX) = segment * 16
        shr     al,4                    ; (AL) = high nibble of segment
        add     dx,[bx].AddrOff.LowWord
        adc     ax,[bx].AddrOff.HighWord
        clc
EndProc

;***    SelOffToLin - selector:offset to linear address
;
;       Pcall   SelOffToLin
;
;       Convert a selector:offset to linear address
;
;       BUGBUG: GRAN4K is ignored in the expand-down limit check.
;
;       ENTRY:  (BX) = pointer to AddrS struc
;               (CX) = length of region, 0 == no limit check
;
;       EXIT:   if carry clear,
;                       (AX:DX) = linear address.
;                       (CX) = valid length of linear region, 0 == 64K
;               if carry set,
;                       (AX) = error code
;
;       USES:   AX, CX, DX, FLAGS
;
        assumeDATA <ds, es, ss>

DefProc SelOffToLin, NEAR
        SaveReg <si, di, bp>
        SaveReg <bx, cx>
        Pcall   GetAddrInfo             ; (AX:DX) = linear base address
        mov     bp,bx                   ; save access and attribute bytes
        RestReg <cx, bx>                ; (SI:DI) = limit
        jc      short sopx

;       So we know how to compare offset against segment limit, see if
;       it's an expand down data segment.

        and     bp,D_SEG+D_CODE+D_EXPDN ; mask to relevant bits
        cmp     bp,D_SEG+D_DATA+D_EXPDN ; is it expand down segment?
        je      short sop20             ; yes, do check for > seg limit

;       Expand Up.

        Pcall   CompareLong, <[bx].AddrOff.LowWord,[bx].AddrOff.HighWord,di,si>
        ja      short sop10             ; jump if offset > limit

        sub     di,[bx].AddrOff.LowWord
        sbb     si,[bx].AddrOff.HighWord; (SI:DI) = bytes left in segment
        or      si,si
        jnz     short sop30             ; jump if (SI) is !0, (SI:DI) > (CX)

        inc     di
        jz      short sop30             ; jump if (DI) == 0ffffh
        jcxz    short sop30             ; jump if no limit check requested

        cmp     di,cx
        jae     short sop30             ; jump if (SI:DI) >= (CX)

sop10:  mov     ax,DEBERR_OFFSET_PAST_LIMIT
        stc
        jmp     short sopx

;       Expand down.

;       BUGBUG: rickdew (5-20-89) Expand-Down limit checks disabled.
;       This causes a problem when using 'db TASKAREA:0' - the limit
;       check is completely bypassed.  This was done to grant access
;       to symbols in TASKAREA.  We need to fix ls, ln, etc, to
;       properly enable this check again.

sop20:
;;      Pcall   CompareLong, <[bx].AddrOff.LowWord,[bx].AddrOff.HighWord,di,si>
;;      jbe     short sop10             ; jump to error if offset <or= limit

sop30:  add     dx,[bx].AddrOff.LowWord
        adc     ax,[bx].AddrOff.HighWord
        clc
sopx:   RestReg <bp, di, si>
EndProc

;***    GetAddrInfo - get selector address info
;
;       Pcall   GetAddrInfo
;
;       ENTRY:  (BX) = pointer to AddrS struc
;
;       EXIT:   if carry clear, no errors
;                       (AX:DX) = base linear address
;                       (SI:DI) = limit
;                       (BL) = access byte
;                       (BH) = attribute byte
;               if carry set,
;                       (AX) = error code
;
;       USES:   AX, BX, CX, DX, SI, DI, FLAGS
;
        assumeDATA <ds, es, ss>

DefProc GetAddrInfo, NEAR, PUBLIC
        Pcall   GetAddrType             ; (AL) = address type
        cmp     al,EXPR_TYPE_SEL
        jne     gai2

        push    es
        mov     es,_ptrGDT+2            ; get the selector of the GDT
        mov     cx,_sizeGDT             ; get the limit of the GDT

;       (ES:0) = Sel:offset address of GDT

        mov     si,[bx].AddrTask        ; (SI) = task number
        mov     dx,[bx].AddrSeg         ; (BX) = selector
        test    dl,TABLE_MASK           ; is selector in LDT?
        jz      gai1                    ; jump if GDT selector

        mov     es,_ptrLDT+2            ; get the selector of the LDT
        mov     cx,_sizeLDT

gai1:   and     dx,0fff8h               ; get rid of useless bits
        cmp     cx,dx                   ; is it a valid selector?
        ja      @F
;
; invalid selector, return error
;
        mov     ax,DEBERR_INVALID_SELECTOR
        pop     es
        stc
        jmp     short gaix

@@:     mov     bx,dx
        mov     ax,es:[bx]              ; get the limit (0-15)
        mov     dl,es:6[bx]             ; get the limit (16-19)
        mov     cl,dl
        and     dx,0fh
        test    cl,80h                  ; byte or page granular?
        jz      @F                      ; byte granular
        mov     cx,4096
        mul     cx                      ; multiply limit by 4096

; 06/23/93
; davidj
; in 32bit addresses where address is used with big selectors
; ie 5b:1abcdeff and the limit as 1bfff000 the 1 would get lost in
; the multiplication
        jnc     @F
        or      dx, 1000h               ; carry was produced
; davidj

@@:     mov     si,dx
        mov     di,ax                   ; get the 32-bit limit in SI:DI
        mov     ah,es:[bx].DescBaseExt
        mov     al,es:[bx].DescBaseHigh
        mov     dx,es:[bx].DescBaseLow  ; (AX:DX) = base linear address
        mov     bx,word ptr es:[bx].DescAccess   ; (BL) = access byte
        pop     es                               ; (BH) = attr byte
        clc                             ; happy return code
        jmp     short gaix

gai2:   mov     ax,DEBERR_INVALID_ADDR_TYPE
        stc
gaix:
EndProc

;***    GetDescInfo - get descriptor information
;
;       Pcall   GetDescInfo
;
;       ENTRY:  (AX:DX) = linear address of table
;               (CX) = table limit
;               (BX) = selector
;               (SI) = task number
;
;       EXIT:   if carry clear, no errors
;                       (AX:DX) = base linear address
;                       (SI:DI) = limit
;                       (BL) = access byte
;                       (BH) = attribute byte
;               if carry set,
;                       (AX) = error code
;
;       USES    AX, BX, CX, DX, SI, DI, FLAGS
;
        assumeDATA <ds, es, ss>

DefProc GetDescInfo, NEAR, LOCAL
        SaveReg <ax, bx, cx, dx, si>
        mov     ax,si                   ; (AX) = task number
;        Pcall   DebCallOut, <DEB_CALLOUT_GETDESCINFO>
        jnc     gdi3                    ; jump if got a descriptor
        or      ax,ax
        stc
        jnz     gdi3                    ; jump if error code
        RestReg <si, dx, cx, bx, ax>

        and     bl,not RPL_MASK
        or      bx,bx                   ; jump if null gdt selector
        jz      gdi4

        and     bl,SEL_LOW_MASK
        cmp     bx,cx                   ; jump if outside table
        jae     gdi4

        add     dx,bx
        adc     ax,0                    ; (AX:DX) = linear address

        mov     bx,si                   ; (BX) = task number
        mov     cx,(size Desc)
        Pcall   LinToPhy                ; (AX:DX) = physical address
        jc      gdix

        push    es
        mov     cx,REG_ESDI
        Pcall   PhyToPtr                ; (ES:DI) = pointer to desc entry
    ASSUME   ES:NOTHING

        mov     bl,es:[di].DescAccess   ; (BL) = access byte
        mov     bh,es:[di].DescAttr     ; (BH) = attribute

        mov     ah,es:[di].DescBaseExt
        mov     al,es:[di].DescBaseHigh
        mov     dx,es:[di].DescBaseLow  ; (AX:DX) = base linear address

        mov     si,word ptr es:[di].DescAttr
        and     si,D_EXTLIMIT           ; (SI) = high word of limit
        mov     di,es:[di].DescLimit    ; (DI) = low word of limit
        pop     es

        test    bh,D_GRAN4K
        jz      gdi2

        mov     cx,12                   ; (CX) = 12 (4K)
gdi1:   shl     di,1
        rcl     si,1
        loop    gdi1
        or      di,0fffh                ; 12 bits were zero filled

gdi2:   Pcall   CheckAccess
        jmp     short gdix

gdi3:   pop     cx                      ; remove the four registers saved
        pop     cx                      ;  without trashing the flags
        pop     cx
        pop     cx
        pop     cx
        jmp     short gdix

gdi4:   mov     ax,DEBERR_INVALID_SELECTOR
        stc
gdix:
EndProc

;***    CheckAccess - check for valid attribute/access byte
;
;       Checks for invalid attribute/access byte
;
;       ENTRY:  (BX) = attribute/access byte
;
;       EXIT:   If carry clear, descriptor ok
;               If carry set, (AX) = error code
;
;       USES:   AX, FLAGS
;
        assumeDATA <ds, es, ss>

DefProc CheckAccess, NEAR, LOCAL
        push    bx
        mov     bh,bl                   ; (BH) = copy of access byte

        test    bl,D_SEG                ; check if valid segment desc
        jnz     short cka1

        and     bl,not (D_PRES + D_DPL3)
        cmp     bl,D_LDT
        je      short cka1

        and     bl,not (D_32 + D_TSS_BUSY_BIT)
        cmp     bl,D_TSS
        je      short cka1

        mov     ax,DEBERR_INVALID_SELECTOR
        stc
        jmp     short ckax

cka1:   test    bh,D_PRES
        jnz     short cka2

        mov     ax,DEBERR_SELECTOR_NOT_PRESENT
        stc
        jmp     short ckax

cka2:   clc
ckax:   pop     bx
EndProc

;***    LinToPhy - Gets an physical address from an linear
;
;       Extracts the physical address from the page directory
;       and table entries for the linear address.  Validates
;       that the page tables and pages are present.  If the
;       region extends across two pages, both pages are validated,
;       but the region length is adjusted to include only the
;       first page.  The higher level routines will remap after
;       the first page is done.  The second page is validated
;       and an error is returned if not present so the higher
;       level routines can return an error for the whole region
;       instead of copying half and then returning an error (on
;       the second page if invalid).
;
;       ENTRY:  (AX:DX) = linear address
;               (BX) = task number
;               (CX) = length of linear region (<=4K), 0 == no limit check
;
;       EXIT:   If carry clear,
;                       (AX:DX) = physical address
;                       (CX) = valid length of physical region, 0 == 64K
;               If carry set,
;                       (AX) = error code
;
;       USES:   AX, DX, CX, FLAGS
;
        assumeDATA <ds, es, ss>

DefProc LinToPhy, NEAR, LOCAL
        SaveReg <bx, cx, si, di, es>
        push    ds
        push    offset tmpDword     ; returned location of physical address
        push    ax
        push    dx                  ; passed linear address
        call    _LinearToPhysical
        add     sp,8                ; clean up passed parameters
        cmp     ax,0                ; error?
        jz      @F
        mov     ax,DEBERR_INVALID_ADDRESS       ; set up return code
        stc
        jmp     short linx
@@:     mov     si,offset tmpDword
        mov     dx,[si]             ; get the low word
        mov     ax,2[si]            ; and the high word
        clc
linx:   RestReg <es, di, si, cx, bx>

EndProc

;***    GetPhy - Returns physical address from a PTE, given a linear address
;
;       ENTRY:  (EAX) = linear address
;               (BX) = task number
;
;       EXIT:   If carry clear,
;                       (EAX) = linear address
;                       (EDX) = physical address
;               If carry set,
;                       (AX) = error code
;
;       USES:   ECX, EDX, ESI, EDI, FLAGS
;
;       GetPhy(LinAddr, TaskNum)
;       {
;           PDE, PDEPhy = GetPDE(LinAddr, TaskNum)
;           if(error) {
;               return error
;           }
;           PTE, PTEPhy = GetPTE(PDEPhy, LinAddr, TaskNum)
;           if(error) {
;               return error
;           }
;           return(PTEPhy)
;       }
;
        assumeDATA <ds, es, ss>

DefProc GetPhy, NEAR, LOCAL
.386p
        push    eax
        Pcall   GetPDE                  ; (EDX) = PDEPhy or (AX) = err code
        pop     esi
        jc      short gfrx
        mov     eax,esi                 ; (EAX) = linear address
        push    eax
        Pcall   GetPTE                  ; (EDX) = PTEPhy or (AX) = err code
        pop     esi
        jc      short gfrx
        mov     eax,esi                 ; (EAX) = linear address
gfrx:
.286p
EndProc

;***    GetPDE - Returns the PDE for the linear address
;
;       ENTRY:  (EAX) = linear address
;               (BX)  = task number
;
;       EXIT:   If carry clear,
;                       (ECX) = PDE
;                       (EDX) = physical address from PDE
;               If carry set,
;                       (ECX) = PDE
;                       (AX) = error code
;
;       USES:   EAX, ECX, EDX, ESI, EDI, FLAGS
;
;       GetPDE(LinAddr, TaskNum)
;       {
;           PDE = DebCallOut(DEB_CALLOUT_GETPDE, LinAddr, TaskNum)
;           if(error) {
;               if(error code == 0) {
;                   PDEPhy = RegSA.CR3sav + LinAddr.LinPageDir
;                   PDE = *PhyToPtr(PDEPhy)
;               }
;               else {
;                   return(error)
;               }
;           }
;           else {
;               PTEPhy = ConvertPTEToPhy(linaddr, PDE, TaskNum)
;               if(error) {
;                   return(error)
;               }
;           }
;       }
;
        assumeDATA <ds, es, ss>

DefProc GetPDE, NEAR, PUBLIC
.386p
        push    bx                      ; save task number, linear address
        push    eax
        Pcall   DebCallOut, <DEB_CALLOUT_GETPDE>
        pop     esi                     ; (ESI) = linear address
        pop     bx                      ; (BX) = task number
        jnc     short gpd10             ; jump if got a PDE
        or      ax,ax
        stc
        jnz     short gpdx              ; jump if error code

        push    esi                     ; save linear address
        and     esi,mask LinPageDir
        shr     esi,LinPageDir - PTE_SHIFT ; (ESI) = page directory index

;        mov     eax,RegSA.CR3sav
;        and     eax,mask PhyPageFrame   ; (EAX) = page directory physical addr
;        add     eax,esi                 ; (EAX) = physical PDE address
;
;        push    bx
;        mov     dx,ax
;        shr     eax,16                  ; (AX:DX) = physical PDE address
;        mov     cx,REG_ESDI
;        Pcall   PhyToPtr                ; (ES:DI) = pointer to PDE
;        mov     ecx,es:[di]             ; (ECX) = PDE
;        pop     bx
        push    es
        mov     es,_ptrDIR+2            ; pointer to page directory
        mov     ecx,es:[si]             ; (ECX) = PDE
        pop     es
        pop     esi                     ; restore linear address
gpd10:  mov     eax,esi                 ; (eax) = linear address

;       (edx)=physaddr = ConvertPTEToPhy(eax=linaddr,ecx=PDE,bx=taskno)

        Pcall   ConvertPTEToPhy         ; (EDX) = physical address from PDE
        mov     ax,DEBERR_PAGE_TABLE_NOT_PRESENT
gpdx:
.286p
EndProc

;***    GetPTE - Returns the PTE for the linear address
;
;       ENTRY:  (EAX) = linear address
;               (EDX) = physical address from PDE
;               (BX)  = task number
;
;       EXIT:   If carry clear,
;                       (ECX) = PTE
;                       (EDX) = physical address from PTE
;               If carry set,
;                       (ECX) = PTE
;                       (AX) = error code
;
;       USES:   EAX, ECX, EDX, ESI, EDI, FLAGS
;
;       GetPTE(PDEPhy, LinAddr, TaskNum)
;       {
;           PDEPhy += LinAddr.LinPageTable
;           PTE = *PhyToPtr(PDEPhy)
;           PTEPhy = ConvertPTEToPhy(linaddr, PTE, TaskNum)
;           if(error) {
;               return(error)
;           }
;           return(PTE, PTEPhy)
;       }
;
        assumeDATA <ds, es, ss>

LocVar  junk, DWORD
DefProc GetPTE, NEAR, PUBLIC
.386p
        push    eax                     ; save linear address
        push    edx                     ; and physical address
        and     eax,mask LinPageTable
        shr     eax,LinPageTable - PTE_SHIFT ; (EAX) = page table index
        add     eax,edx                 ; (EAX) = physical PTE address


        push    ds
        lea     dx,junk
        push    dx                      ; junk dword
        push    4                       ; number of bytes to get
        push    eax                     ; physical address
        call    _GetAddress             ; read this data
        add     sp,10                   ; clean up parameters

        mov     ecx,[junk]              ; (ECX) = PTE
        pop     edx                     ; restore physical address
        pop     eax                     ; restore linear address

;       (edx)=physaddr = ConvertPTEToPhy(eax=linaddr,ecx=PTE,bx=taskno)

        Pcall   ConvertPTEToPhy         ; (EDX) = physical address from PTE
.286p
EndProc

;***    ConvertPTEToPhy - return the physical address from a PDE or PTE
;
;       ENTRY:  (EAX) = linear address
;               (ECX) = PDE or PTE
;               (BX)  = task number
;
;       EXIT:   If carry clear,
;                       (EDX) = physical address
;               If carry set,
;                       (AX) = error code
;
;       USES:   EAX, EDX, ESI, EDI, FLAGS
;
;       ConvertPTEToPhy(PTE, TaskNum)
;       {
;           if(PTE.PTE_Present) {
;               return(PTE.PTE_PageFrame)
;           }
;           return(DebCallOut(DEB_CALLOUT_GETFRAME, PTE, TaskNum))
;       }

        assumeDATA <ds, es, ss>

DefProc ConvertPTEToPhy, NEAR, LOCAL
.386p
        mov     edx,ecx
        and     edx,mask PTE_PageFrame  ; (EDX) = physical page address
        .errnz (PTE_PageFrame - PhyPageFrame)
        test    ecx,mask PTE_Present    ; jump if page present, clears carry
        jnz     short cppx

        push    bx                      ; save task number, linear address
        push    ecx

;       (edx)=frame = *(callout[GETFRAME])(eax=linaddr,ecx=PTE/PDE,bx=taskno)

        Pcall   DebCallOut, <DEB_CALLOUT_GETFRAME>
        pop     ecx                     ; (ECX) = PDE or PTE
        pop     bx                      ; (BX) = task number
        jc      short cppx              ; if carry set, (AX) = error code
        shl     edx,PhyPageFrame        ; (EDX) = physical page address
cppx:
.286p
EndProc

;***    PhyToPtr - Physical to pointer
;
;       Convert physical address to a pointer.
;
;       Pcall   PhyToPtr
;
;       ENTRY:  (AX:DX) = physical address
;               (CX) = register set flag
;
;       EXIT:   if (CX) == REG_DSSI (0), (DS:SI) = pointer
;               if (CX) == REG_ESDI (!0), (ES:DI) = pointer
;
;       USES:   AX, BX, DX, FLAGS
;               if (CX) == REG_DSSI uses SI, DS
;               if (CX) == REG_ESDI uses DI, ES
;
        assumeDATA <ds, es, ss>

DefProc PhyToPtr, NEAR, LOCAL
        Pcall   IsDebProtMode
        jc      short ptp1              ; jump if debugger is in protected mode
        Pcall   PhyToSegOff
        jmp     short ptpx

ptp1:   Pcall   PhyToLin                ; (AX:DX) = linear address
        Pcall   LinToSelOff
ptpx:
EndProc

;***    PhyToSegOff - physical address to segment:offset
;
;       Converts a physical address to real mode segment:offset.
;
;       Pcall   PhyToSegOff
;
;       ENTRY:  (AX:DX) = physical address
;               (CX) = register set flag
;
;       EXIT:   if (CX) == REG_DSSI (0), (DS:SI) = segment:offset
;               if (CX) == REG_ESDI (!0), (ES:DI) = segment:offset
;
;       USES:   AX, BX, DX, FLAGS
;               if (CX) == REG_DSSI uses SI, DS
;               if (CX) == REG_ESDI uses DI, ES
;
;       Real mode only.
;
        assumeDATA <ds, es, ss>

DefProc PhyToSegOff, NEAR, LOCAL
        test    ax,0fff0h               ; check if over 1 meg
        jz      short psg1
;        Pcall   DoLoadAll
        jmp     short psg3

;       (AX:DX) = physical address

psg1:   mov     bx,dx                   ; (BX) = low word
        and     bx,0fh                  ; the offset is the lower 4 bits
        shr     dx,4                    ; move high 12 bits to low 12 bits
        shl     al,4                    ; move low nybble to high nybble
        or      dh,al                   ; add in high 4 bits of address

;       Check what register to use

        jcxz    short psg2              ; jump if DS:SI register set
        .errnz  REG_DSSI

;       ES:DI is to be set up

        mov     es,dx
        mov     di,bx
    ASSUME   ES:NOTHING
        jmp     short psg3

;       DS:SI is to be set up

psg2:   mov     ds,dx
        mov     si,bx
    ASSUME   DS:NOTHING
psg3:
EndProc

;***    PhyToLin - convert physical to an linear address
;
;       Converts a physical address to a linear address. Doesn't
;       do anything if paging is disabled.
;
;       Pcall   PhyToLin
;
;       ENTRY:  (AX:DX) = physical address
;
;       EXIT:   (AX:DX) = linear address
;
;       USES:   EAX, EBX, EDX, FLAGS
;
;       Protected mode only.
;
;       PhyToLin(PhyAddr)
;       {
;           if(paging enabled) {
;               ppte = DebData.DebppteSpare
;               fppte = LinToSelOff(ppte)
;               fppte->PTE_PageFrame = PhyAddr.PhyPageFrame
;               fppte->PTE_User = 0
;               fppte->PTE_Write = 1
;               fppte->PTE_Present = 1
;               flush TLB
;               return(DebData.DebptelinSpare + PhyAddr.PhyDataOffset)
;           }
;       }
;
        assumeDATA <ds, es, ss>

DefProc PhyToLin, NEAR, LOCAL
.386p
        Pcall   IsPagingEnabled
        jnc     short ptlx              ; jump if no paging
        SaveReg <cx, di>

;       Get a pointer to the spare PTE

        push    ax                      ; save physical address
        push    dx
        mov     ax,DebData.DebppteSpare.HighWord
        mov     dx,DebData.DebppteSpare.LowWord
        mov     cx,REG_ESDI
        Pcall   LinToSelOff             ; (ES:DI) = PTEptr
    ASSUME   ES:NOTHING
        pop     ebx                     ; (EBX) = physical address

;       PTE_PageFrame = PhyAddr.PhyPageFrame

        mov     eax,ebx                 ; (EAX) = physical address
        and     eax,mask PhyPageFrame
        .errnz  PhyPageFrame - PTE_PageFrame

;       PTE_User already clear, set PTE_Write and PTE_Present

        or      eax,mask PTE_Write + mask PTE_Present
        mov     es:[di],eax             ; change the spare PTE
        DataContext <es>,dx

;       Flush TLB

        mov     eax,cr3
        mov     cr3,eax

;       Get the linear address to PTE represents

        and     ebx,mask PhyDataOffset  ; (EBX) = offset into page
        .errnz  PhyDataOffset
        add     ebx,DebData.DebptelinSpare ; (EBX) = final linear address

        mov     dx,bx
        shld    eax,ebx,16              ; (AX:DX) = final linear address
        RestReg <di, cx>
ptlx:
.286p
EndProc

;***    LinToSelOff - linear address to selector:offset
;
;       Converts a linear address to protected mode selector:offset.
;
;       Pcall   LinToSelOff
;
;       ENTRY:  (AX:DX) = linear address
;               (CX) = register set flag
;
;       EXIT:   if (CX) == REG_DSSI (0), (DS:SI) = pointer
;               if (CX) == REG_ESDI (!0), (ES:DI) = pointer
;
;       USES:   AX, BX, FLAGS
;               if (CX) == REG_DSSI uses SI, DS
;               if (CX) == REG_ESDI uses DI, ES
;
;       Protected mode only.
;
        assumeDATA <ds, es, ss>

DefProc LinToSelOff, NEAR, LOCAL
        test    DebData.InitFlag,INIT_CPU386
        jz      short lsl10             ; jump if 286

;       Must have operand override to save/load all 32 bits

        db      OPER_OVERRIDE
        sgdt    DTRtemp
        db      OPER_OVERRIDE
        lgdt    ModeSwGdtAddr           ; load modesw GDT address
        jmp     short lsl20

lsl10:  sgdt    DTRtemp
        lgdt    ModeSwGdtAddr           ; load modesw GDT address
        xor     ah,ah                   ; force high byte to 0 on 286

;       (AX:DX) = address
;       (CX) = register flag

lsl20:  mov     ModeSwGdtSpare.DescBaseExt,ah
        mov     ModeSwGdtSpare.DescBaseHigh,al
        mov     ModeSwGdtSpare.DescBaseLow,dx
        mov     bx,MODESW_SPARESEL

;       Check what register to use

        jcxz    short lsl30
        .errnz  REG_DSSI

;       ES:DI is to be set up

        mov     es,bx
        xor     di,di                   ; (ES:DI) = alias pointer
    ASSUME   ES:NOTHING
        jmp     short lsl40

;       DS:SI is to be set up

lsl30:  mov     ds,bx
        xor     si,si                   ; (DS:SI) = alias pointer
    ASSUME   DS:NOTHING

lsl40:  test    ss:DebData.InitFlag,INIT_CPU386 ; MUST USE SS OVERRIDE
        jz      short lsl50             ; jump if 286
        db      OPER_OVERRIDE
lsl50:  lgdt    ss:DTRtemp              ; restore GDT - MUST USE SS OVERRIDE
EndProc

;***    GetAddrType - returns the address type
;
;       Returns the address type from an AddrS struc
;
;       ENTRY:  (BX) = pointer to AddrS struc
;
;       EXIT:   (AL) = address type
;
;       USES:   AX, Flags
;
        assumeDATA <ds, es, ss>

DefProc GetAddrType, NEAR, PUBLIC
        mov     al,[bx].AddrType        ; (AL) = address type
        test    al,EXPR_TYPE_DEF
        jz      short @F
        mov     al,DefType.LowByte      ; (AL) = default address type
@@:
EndProc

;***    GetAddrTask - returns the address task number
;
;       Returns the address task number from an AddrS struc
;
;       ENTRY:  (BX) = pointer to AddrS struc
;
;       EXIT:   (AX) = address type
;
;       USES:   AX, Flags
;
        assumeDATA <ds, es, ss>

DefProc GetAddrTask, NEAR, PUBLIC
        mov     ax,[bx].AddrTask        ; (AX) = address type
        or      ax,ax
        jnz     short gakx
        Pcall   GetCurrTask             ; (AX) = current task number
gakx:
EndProc

;***    GetCurrTask
;
;       ENTRY:  NONE
;
;       EXIT:   (AX) = current task number
;
;       USES:   AX, Flags
;
        assumeDATA <ds, es, ss>

DefProc GetCurrTask, NEAR, PUBLIC
;        Pcall   DebCallOut, <DEB_CALLOUT_GETCURRTASK>
EndProc

;***    SegToLin - convert segment to linear address
;
;       Converts a real mode segment value to a linear address
;
;       ENTRY:  (DX) = segment
;
;       EXIT:   (AX:DX) = linear address
;
;       USES:   AX, DX, Flags
;
        assumeDATA <ds, es, ss>

DefProc SegToLin, NEAR, LOCAL
        mov     al,dh                           ; (AL) = high byte of segment
        shl     dx,4                            ; (DX) = segment * 16
        shr     al,4                            ; (AL) = high nibble of segment
        xor     ah,ah                           ; (AX:DX) = linear address
EndProc

;***    IsPagingEnabled - returns indication of paging state
;
;       ENTRY:  NONE
;
;       EXIT:   Carry set, paging is enabled
;               Carry clear, paging is not enabled
;
;       USES:   Flags
;
        assumeDATA <ds, es, ss>

DefProc IsPagingEnabled, NEAR, PUBLIC
        push    ax
        mov     ax,RegSA.CR0sav.HighWord        ; P000 0000b
        shl     ax,1                            ; CY <- P000 0000
        pop     ax
EndProc

;***    WhatIsClientMode - determine debuggee mode (real, prot, v86)
;
;       ENTRY:  REAL mode -or-
;               PROT mode -or-
;               V86 mode
;
;       EXIT:   Use following conditional jumps to test state:
;               JC  foo     ; PROT mode
;               JZ  foo     ; REAL mode
;               JA  foo     ; V86 mode
;               JNC foo     ; Not PROT mode (either V86 or real mode)
;
;       USES:   Flags modified, everything else preserved
;
        assumeDATA <ds, es, ss>

DefProc WhatIsClientMode, NEAR, PUBLIC
        push    ax
        push    bx
        mov     ax,RegSA.MSWsav         ; (ax) = debuggee's MSW
        and     ax,01b                  ; (ax) = 0000 0000 0000 000P
        xor     al,01b                  ; (ax) = 0000 0000 0000 000!P
        mov     bx,RegSA.EFsav.HighWord ; (bx) = high word of eflags
        and     bx,10b                  ; (bx) = 0000 0000 0000 00V0
        or      ax,bx                   ; (ax) = 0000 0000 0000 00V!P

;*  (AX) = 0, prot mode
;        = 1, real mode
;        = 2, v86 mode

        cmp     ax,1                    ; Set flags
        pop     bx
        pop     ax
EndProc

;***    IsDebProtMode - determine debugger is in real or protected mode
;
;       This routine checks the machine status word on
;       the 286 and determines if the cpu is in real or
;       protected mode.
;
;       ENTRY   None
;
;       EXIT    Carry flag set if in protected mode, clear otherwise
;
;       USES    Flag modified, everything else preserved
;
;       WARNING This routine may be called in real or protected mode.
;
        assumeDATA <ds, es, ss>

DefProc IsDebProtMode, NEAR, PUBLIC

;       All we have to do is copy the low bit of the machine
;       status word into the carry flag.

        push    ax                      ; save caller's ax
        smsw    ax                      ; AX = machine status word
        shr     al,1                    ; cy flag = cpu mode bit
        pop     ax                      ; restore caller's AX
EndProc

;***    Is286 - determine if we're on a 286 or 8086/88
;
;       See if we're running on a 286 or 8088/86
;
;       ENTRY   None
;
;       EXIT    Z = 1 if 8088/86, Z = 0 if 286
;
;       USES    flags modified, other regs preserved
;
;       WARNING This routine relies on the fact that PUSH SP on a 286
;               pushes the old SP, whereas an 8088/86 pushes the new one.
;               This code can be executed on an 8088, or a 286. On a
;               286 it can be called in real or protected mode.
;
        assumeDATA <ds, es, ss>

DefProc Is286, NEAR, PUBLIC
        push    bp                      ; save BP
        push    sp
        mov     bp,sp
        cmp     bp,[bp]                 ; compare SP with saved SP
        pop     bp                      ; clean SP off stack
        pop     bp                      ; restore BP
EndProc

;***    PopFlags - pops flags without the bug in the 286
;
;       This routine is called by the POPFF macro after pushing
;       cs and making a near call.
;
;       ENTRY:  TOS = iret frame
;
;       EXIT:   NONE
;
;       USES:   FLAGS modified, other regs preserved
;
        assumeDATA <ds, es, ss>

DefProc PopFlags, NEAR, PUBLIC
        iret
EndProc NoGen

.386p

;***    GetObjectTable
;
;       Pcall   GetObjectTable
;
;       ENTRY   (BX)  = pointer to AddrS
;               (CX)  = laddr flag: 0 implies handle
;               (EDX) = handle/laddr
;       EXIT    If carry clear, no errors
;                       (EDX) = handle
;               If carry set,
;                       (AX) = error code
;       USES    EAX, EDX, Flags

DefProc GetObjectTable,NEAR,LOCAL
    assumeDATA <ds, es, ss>
        push    es
        les     di, _ptrSASvm
        mov     eax,dword ptr es:[di+4] ; [_pobvmOne] (should be struct)
        pop     es

        jcxz    short got00             ; jmp if handle given

        sub     edx,eax                 ; else convert laddr to handle
        jc      short got80
        push    eax
        push    ebx
        mov     eax,edx
        xor     edx,edx
        mov     ebx,size vmob_s - obu0  ; (ebx) = size
        idiv    ebx                     ; (eax) = (edx:eax)/size
        mov     edx,eax                 ; (edx) = handle
        pop     ebx
        pop     eax
        inc     edx

got00:  or      edx,edx
        .errnz  HOBNULL
        jz      short got80
        dec     edx                     ; (edx) = handle - 1
;       add     eax,edx                 ; (eax) += handle-1 * size vmob_s
        lea     eax,[eax+edx*8]
        lea     eax,[eax+edx*8]
        .errnz (size vmob_s - obu0 - (1 + 8 + 7))
        inc     edx                     ; restore edx
        Pcall   FillLinAddr, <bx, eax>
        clc
        jmp     short gotx

got80:  mov     ax,DEBERR_OFFSET_PAST_LIMIT
        stc
gotx:
EndProc

;***    GetArenaTable
;
;       Pcall   GetArenaTable
;
;       ENTRY   (BX)  = pointer to AddrS
;               (CX)  = laddr flag: 0 implies handle
;               (EDX) = handle/laddr
;       EXIT    If carry clear, no errors
;                       (EDX) = handle
;               If carry set,
;                       (AX) = error code
;       USES    EAX, EDX, Flags

DefProc GetArenaTable,NEAR,LOCAL
    assumeDATA <ds, es, ss>

        push    es
        les     di, _ptrSASvm
        mov     eax,dword ptr es:[di] ;  [_parvmOne] (should be struct)
        pop     es

        jcxz    short gat00             ; jmp if handle given

        sub     edx,eax                 ; else convert laddr to handle
        jc      short gat80
        push    eax
        push    ebx
        mov     eax,edx
        xor     edx,edx
        mov     ebx,size vmar_s - aru4  ; (ebx) = size
        idiv    ebx                     ; (eax) = (edx:eax)/size
        mov     edx,eax                 ; (edx) = handle
        pop     ebx
        pop     eax
        inc     edx

gat00:  or      edx,edx
        .errnz  HARNULL
        jz      short gat80
        push    edx
        push    ecx
        dec     edx                     ; (edx) = handle - 1= index
        lea     ecx, [edx*2]            ; (ecx) = index*2
        lea     edx,[edx+edx*4]         ; (edx) = index*5
        lea     edx,[ecx+edx*4]         ; (edx) = index * (size vmar_s)
        .errnz  (size vmar_s - aru4 -(4 * (1+4)) - 2)
        lea     eax,[eax+edx]           ; (eax) = laddr of arena record
        pop     ecx
        pop     edx                     ; restore edx
        Pcall   FillLinAddr, <bx, eax>
        clc
        jmp     short @F

gat80:  mov     ax,DEBERR_OFFSET_PAST_LIMIT
        stc
@@:
EndProc

CodeSegmentEnd

        end
