;       SCCSID = @(#)debprt.asm 6.1 90/11/17
PAGE    ,132
NAME    DEBPRT
TITLE   DEBPRT.ASM - debugger table printer
.286p

;***    DEBPRT.ASM - debugger table printer and modifier interpreter
;
;       SCCSID = @(#)debprt.asm 13.6 89/10/24
;
;       This is the debugger's table printer threaded code
;       interpreter. There is two levels of interpretation done
;       here.  Externally this module is given a list of opcodes
;       and data that it interprets with a set thread programs (one
;       for each opcode). Some definations of the terms use:
;
;       OpCode - this is the code used in the externally
;       defined format list. The op code value is used to
;       get the start of it's thread program.
;
;       Thread Program - a list of thread codes that determine
;       what a opcode is to do.
;
;       Thread Code - a byte used to build thread programs that
;       are interpreted. The thread code points to it's thread
;       address.
;
;       Thread Address - this is the address of a routine that is
;       to be executed for it's thread code.
;
;
; 155531    RJM 23 May 1996 Included SMP routines for .MK
;

.xlist
        include debseg.inc
        include debmac.inc
        include debequ.inc
        include debdesc.inc
        include debinit.inc
.list

DataSegmentStart

Extern  pTXT, WORD
Extern  DebData, WORD

;***    StartOpTables
;
;       This initializes the variables needed to generate the
;       thread tables. There are three tables created after all
;       "ThreadProgram" macros and the "EndOpTables" macro is
;       processed. The first table (OpCodeTbl) is a word wide
;       table of offsets into the next table (ThreadProgTbl). The
;       Op Code from the format table is used to index the "OpCodeTbl"
;       which points to a list of byte offsets in the last table
;       (ThreadAddrTbl). Each byte "ThreadProgTbl" list is used to index
;       the "ThreadAddrTbl" which is an address of a routine to call.
;       So the "ThreadProgTbl" contains thread programs for each op code
;       in the OpInterpret format specs.
;

StartOpTables   macro
                if1
                        ?OpCodeCnt = 0
                        ?ThCodeCnt = 0
                        ?ThAddrCnt = 0
                endif
                endm

;***    ThreadProgStart
;
;       This macro is used to start a thread program. It defines the
;       name of the Op Code and creates a entry in the Op Code Table.
;

ThreadProgStart macro   name
                if1
                        ?Op&name = ?ThCodeCnt   ;; define name of Op Code offset
                        ?MakeName <dw ?Op&name>, ?OpCode, %?OpCodeCnt
                        _&name = ?OpCodeCnt     ;; define name of new Op Code
                        public _&name           ;; make it public to debugger
                        name = ?OpCodeCnt       ;; local name
                        ?OpCodeCnt = ?OpCodeCnt + 1
                endif
                endm

;***    ThreadProgCode
;
;       This macro generates the entries in the thread code table and
;       if it's not already there, an new address in the thread address
;       table.
;

ThreadProgCode  macro   thprg
                irp     x,<thprg>
                        if .TYPE x EQ 20h       ;; if literal number (1, etc.)
                                ?ThCodeEntry x
                        else
                                ?ThAddrEntry x
                                ?ThCodeEntry ?Th&&x
                        endif
                endm
                endm

;***    ThreadProgEnd
;
;       This macro is used to terminated an thread code program.
;

ThreadProgEnd   macro
                ?ThAddrEntry EndProg
                ?ThCodeEntry ?Th&EndProg
                endm

;***    EndOpTables
;
;       This macro actually builds the tables from the labels
;       generated in the ThreadProgram macro.
;

EndOpTables     macro   OpCodeName, ThProgName, ThAddrName
OpCodeName      label   WORD
                ?cnt = 0
                rept    ?OpCodeCnt
                        ?UseName ?OpCode, %?cnt
                        ?cnt = ?cnt + 1
                endm
ThProgName      label   BYTE
                ?cnt = 0
                rept    ?ThCodeCnt
                        ?UseName ?ThCode, %?cnt
                        ?cnt = ?cnt + 1
                endm
ThAddrName      label   WORD
                ?cnt = 0
                rept    ?ThAddrCnt
                        ?UseName ?ThAddr, %?cnt
                        ?cnt = ?cnt + 1
                endm
                endm

;***    ?ThCodeEntry
;
;       Sets up entries in the thread code program table.
;

?ThCodeEntry    macro   name
                if1
                        ?MakeName <db name>, ?ThCode, %?ThCodeCnt
                        ?ThCodeCnt = ?ThCodeCnt + 1
                endif
                endm

;***    ?ThAddrEntry
;
;       Sets up an entry in the thread address table.
;

?ThAddrEntry    macro   name
                if2
                        ifndef  name
                                CodeSegmentStart
                                extrn   _&name:NEAR
                                name    equ     _&name
                                CodeSegmentEnd
                        endif
                endif
                if1
                        ifndef  ?Th&name
                                ?MakeName <dw name>, ?ThAddr, %?ThAddrCnt
                                ?Th&name = ?ThAddrCnt * 2
                                ?ThAddrCnt = ?ThAddrCnt + 1
                        endif
                endif
                endm

;***    ?UseName, ?MakeName
;
;       Macros to build and use names created from a base name
;       and a number.
;

?UseName        macro   mac, num
                mac&num
                endm

?MakeName       macro   cmd, mac, num
                mac&num &macro
                        cmd
                        &endm
                endm

;***    OpInterpret thread program tables
;
;       Most of the opcode thread programs take arguments after the
;       opcode. The thread programs for the opcode comments what kinds
;       of the arguments are needed.  Defination of the types of opcode
;       arguments:
;
;       offst - a byte offset added to the data pointer (es:di) and
;       the result address is used to get the data to print.
;
;       startbit - starting bit number (0-31) of a bitfield.
;
;       bitwidth - width of bitfield (1-32).
;
;       bitnumber - which bit (0-31) to use if opcode.
;
;       addr - a word used to get data relative to DS.
;
;       string - a NULL terminated literal string.
;
;       opaddr - a word address of a new opcode list to use.
;
;       argnum - a byte from first argument pushed on stack.
;

StartOpTables

;
; This entry defines the end of opcode list token. Takes no arguments.
;

ThreadProgStart EndOp
        ThreadProgCode <EndTable>

;
; This entry defines the end of opcode list token call with arguments.
; Use: EndOpArg, numargs.
;

ThreadProgStart EndOpArg
        ThreadProgCode <GetOpByte, EndTableArg>

;
; Outputs a blank. Takes no arguments.
;

ThreadProgStart OutBlank
        ThreadProgCode <blank>
ThreadProgEnd

;
; Outputs a number of blanks. Use: OutBlanks, number of blanks.
;

ThreadProgStart OutBlanks
        ThreadProgCode <GetOpByte, MovToCX, tab>
ThreadProgEnd

;
; Outputs a cr & lf. Takes no arguments.
;

ThreadProgStart OutCrLf
        ThreadProgCode <crlf>
ThreadProgEnd

;
; Outputs a character. Use: OutChar, character.
;

ThreadProgStart OutChar
        ThreadProgCode <GetOpByte, MovToAX, put_ch>
ThreadProgEnd

;
; Outputs a literal string. Use: OutStr, string.
;

ThreadProgStart OutStr
        ThreadProgCode <OutOpString>
ThreadProgEnd

;
; Outputs a forced lower case literal string. Use: OutStrlc, string.
;

ThreadProgStart OutStrlc
        ThreadProgCode <LowerCaseOn, OutOpString>
ThreadProgEnd

;
; Outputs a message from an address. Use: OutMsg, addr.
;

ThreadProgStart OutMsg
        ThreadProgCode <GetOpWord, rprbuf>
ThreadProgEnd

;
; Outputs a lower cased (depending on dislwr) message from
; an address. Use: OutMsglc, addr.
;

ThreadProgStart OutMsglc
        ThreadProgCode <GetOpWord, LowerCaseOn, rprbuf>
ThreadProgEnd

;
; Outputs a message from an address passed as an argument.
; Use: OutMsgArg, argnum.
;

ThreadProgStart OutMsgArg
        ThreadProgCode <GetOpByteBX, GetWordArg, rprbuf>
ThreadProgEnd

;
; Outputs a lower cased (depending on dislwr) message from an address
; passed as an argument.
; Use: OutMsgArglc, argnum.
;

ThreadProgStart OutMsgArglc
        ThreadProgCode <GetOpByteBX, GetWordArg, LowerCaseOn, rprbuf>
ThreadProgEnd

;
; Outputs a string terminated at an address in buffer passed.
; Use: OutStrPtr, offst, maxlen.
;

ThreadProgStart OutStrPtr
        ThreadProgCode <GetOpByteBX, GetOpByte, MovToCX, PutStrPtr>
ThreadProgEnd

;
; Outputs a forced lower case string terminated at an
; address in buffer passed.
; Use: OutStrPtr, offst, maxlen.
;

ThreadProgStart OutStrPtrlc
        ThreadProgCode <GetOpByteBX, GetOpByte, MovToCX, LowerCaseOn, PutStrPtr>
ThreadProgEnd

;
; Outputs a byte from pointer + offst in hex. Use: OutByteHexPtr, offst.
;

ThreadProgStart OutByteHexPtr
        ThreadProgCode <GetOpByteBX, GetBytePtr, MovToAX, hex>
ThreadProgEnd

;;155531
;
; Outputs a byte from an address in hex. Use: OutByteHexAddr, addr.
;

ThreadProgStart OutByteHexAddr
        ThreadProgCode <GetOpWordBX, GetWordAdr, MovToAX, hex>
ThreadProgEnd

;
; Outputs a word from pointer + offst in hex. Use: OutWordHexPtr, offst.
;

ThreadProgStart OutWordHexPtr
        ThreadProgCode <GetOpByteBX, GetWordPtr, out16>
ThreadProgEnd

;155531
;
; Outputs a word from an address in hex. Use: OutWordHexAddr, addr.
;

ThreadProgStart OutWordHexAddr
        ThreadProgCode <GetOpWordBX, GetWordAdr, out16>
ThreadProgEnd

;
; Outputs a word in hex from pointer with an offset from the
; argument list.  Use: OutWordHexArg, argnum.
;

ThreadProgStart OutWordHexArg
        ThreadProgCode <GetOpByteBX, GetWordArgBX, GetWordPtr, out16>
ThreadProgEnd

;
; Outputs 24 bits of data from pointer + offst in hex.
; Use: Out24HexPtr, offst.
;

ThreadProgStart Out24HexPtr
        ThreadProgCode <GetOpByteBX, Get24Ptr, Out24>
ThreadProgEnd

;
; Outputs 24 bits of data in hex from pointer with the offset from
; the argument list.  Use: Out24HexArg, argnum.
;

ThreadProgStart Out24HexArg
        ThreadProgCode <GetOpByteBX, GetWordArgBX, Get24Ptr, Out24>
ThreadProgEnd

;
; Outputs 32 bits of offset from gate descriptor at pointer in hex.
; Use: OutDescBasePtr.
;

ThreadProgStart OutGateOffstPtr
        ThreadProgCode <GetGateOffstPtr>        ; (AX:DX) = gate offset
        ThreadProgCode <SkipIfEnv386, 2>        ; if 32 bit env, skip two ops
        ThreadProgCode <out16, EndProg>         ; print the offset as 16 bits
        ThreadProgCode <Out32>                  ; print the offset as 32 bits
ThreadProgEnd

;
; Outputs 32 bits of limit from descriptor at pointer in hex.
; Use: OutDescLimitPtr.
;

ThreadProgStart OutDescLimitPtr
        ThreadProgCode <GetDescLimitPtr>        ; (AX:DX) = desc limit
        ThreadProgCode <SkipIfEnv386, 2>        ; if 32 bit env, skip two ops
        ThreadProgCode <out16, EndProg>         ; print the limit as 16 bits
        ThreadProgCode <Out32>                  ; print the limit as 32 bits
ThreadProgEnd

;
; Outputs 32 bits of base from descriptor at pointer in hex.
; Use: OutDescBasePtr.
;

ThreadProgStart OutDescBasePtr
        ThreadProgCode <GetDescBasePtr>         ; (AX:DX) = desc base
        ThreadProgCode <SkipIfEnv386, 2>        ; if 32 bit env, skip two ops
        ThreadProgCode <Out24, EndProg>         ; print the limit as 24 bits
        ThreadProgCode <Out32>                  ; print the limit as 32 bits
ThreadProgEnd

;
; Outputs dword of data from pointer + offst in hex.
; Use: OutDwordHexPtr, offst.
;

ThreadProgStart OutDwordHexPtr
        ThreadProgCode <GetOpByteBX, Get32Ptr, Out32>
ThreadProgEnd

;155531
;
; Outputs a dword from an address in hex.
; Use: OutDwordHexAddr, addr.
;

ThreadProgStart OutDwordHexAddr
        ThreadProgCode <GetOpWordBX, GetDwordAdr, Out32>
ThreadProgEnd

;
; Outputs dword of data in hex from pointer with offset from the
; argument list.  Use: OutDwordHexArg, argnum.
;

ThreadProgStart OutDwordHexArg
        ThreadProgCode <GetOpByteBX, GetWordArgBX, Get32Ptr, Out32>
ThreadProgEnd

IFDEF DECIMAL

;
; Outputs byte data in decimal from pointer + offst.
; Use: OutByteDecPtr, offst, numdigits.
;

ThreadProgStart OutByteDecPtr
        ThreadProgCode <GetOpByteBX>    ; (BX) = offst into table
        ThreadProgCode <SaveImm, " ">   ; push pad char (space) on stack
        ThreadProgCode <GetOpByte>      ; (DX) = number of digits
        ThreadProgCode <SaveDX>         ; push number of digits on stack
        ThreadProgCode <SaveImm, 10>    ; push radix (base 10) on stack
        ThreadProgCode <GetBytePtr>     ; (AX:DX) = data from table
        ThreadProgCode <SaveAX, SaveDX> ; push (AX:DX) on stack
        ThreadProgCode <OutRadix>       ; call output routine
ThreadProgEnd

;
; Outputs word data in decimal from pointer + offst.
; Use: OutWordDecPtr, offst, numdigits.
;

ThreadProgStart OutWordDecPtr
        ThreadProgCode <GetOpByteBX>    ; (BX) = offst into table
        ThreadProgCode <SaveImm, " ">   ; push pad char (space) on stack
        ThreadProgCode <GetOpByte>      ; (DX) = number of digits
        ThreadProgCode <SaveDX>         ; push number of digits on stack
        ThreadProgCode <SaveImm, 10>    ; push radix (base 10) on stack
        ThreadProgCode <GetWordPtr>     ; (AX:DX) = data from table
        ThreadProgCode <SaveAX, SaveDX> ; push (AX:DX) on stack
        ThreadProgCode <OutRadix>       ; call output routine
ThreadProgEnd

ENDIF ; DECIMAL

;
; Outputs a bit field in hex from pointer + offst.
; Use: OutBitFHexPtr, offst, startbit, bitwidth, [numdigits]
; Note: "numdigits" is an optional parameter when invoked with
; the macro. Defaults to 0 (no padding).
;

ThreadProgStart OutBitFHexPtr
        ThreadProgCode <GetOpByteBX>    ; (BX) = offst into table
        ThreadProgCode <GetOpWord>      ; (DX) = bitwidth, startbit
        ThreadProgCode <MovToCX>        ; (CX) = bitwidth, startbit
        ThreadProgCode <SaveImm, "0">   ; push pad char ("0") on stack
        ThreadProgCode <GetOpByte>      ; (DX) = number of digits
        ThreadProgCode <SaveDX>         ; push number of digits on stack
        ThreadProgCode <SaveImm, 16>    ; push radix (base 16) on stack
        ThreadProgCode <GetBitFPtr>     ; (AX:DX) = data from table
        ThreadProgCode <SaveAX, SaveDX> ; push (AX:DX) on stack
        ThreadProgCode <OutRadix>       ; call output routine
ThreadProgEnd

;
; Outputs a bit field in hex from pointer + offst from argument stack.
; Use: OutBitFHexArg, argnum, startbit, bitwidth, [numdigits]
; Note: "numdigits" is an optional parameter when invoked with
; the macro. Defaults to 0 (no padding).
;

ThreadProgStart OutBitFHexArg
        ThreadProgCode <GetOpByteBX>    ; (BX) = argument number
        ThreadProgCode <GetWordArgBX>   ; (BX) = offst into table
        ThreadProgCode <GetOpWord>      ; (DX) = bitwidth, startbit
        ThreadProgCode <MovToCX>        ; (CX) = bitwidth, startbit
        ThreadProgCode <SaveImm, "0">   ; push pad char ("0") on stack
        ThreadProgCode <GetOpByte>      ; (DX) = number of digits
        ThreadProgCode <SaveDX>         ; push number of digits on stack
        ThreadProgCode <SaveImm, 16>    ; push radix (base 16) on stack
        ThreadProgCode <GetBitFPtr>     ; (AX:DX) = data from table
        ThreadProgCode <SaveAX, SaveDX> ; push (AX:DX) on stack
        ThreadProgCode <OutRadix>       ; call output routine
ThreadProgEnd

;
; Outputs a string if bit in pointer + offst is set.
; Use: OutMsgIfBitSet, offst, bitnumber, addr
;

ThreadProgStart OutMsgIfBitSet
        ThreadProgCode <GetOpByteBX, GetOpByte, MovToCX, GetOpWord>
        ThreadProgCode <SkipIfBitSetPtr, 1, EndProg, rprbuf>
ThreadProgEnd

;
; Outputs a string if bit in pointer + offst is clear.
; Use: OutMsgIfBitClr, offst, bitnumber, addr
;

ThreadProgStart OutMsgIfBitClr
        ThreadProgCode <GetOpByteBX, GetOpByte, MovToCX, GetOpWord>
        ThreadProgCode <SkipIfBitSetPtr, 1, rprbuf>
ThreadProgEnd

;
; Outputs a string if bit in pointer + argument is set.
; Use: OutMsgIfBitSetArg, argnum, bitnumber, addr
;

ThreadProgStart OutMsgIfBitSetArg
        ThreadProgCode <GetOpByteBX, GetWordArgBX, GetOpByte>
        ThreadProgCode <MovToCX, GetOpWord, SkipIfBitSetPtr, 1>
        ThreadProgCode <EndProg, rprbuf>
ThreadProgEnd

;
; Outputs a string if bit in pointer + argument is clear.
; Use: OutMsgIfBitClrArg, argnum, bitnumber, addr
;

ThreadProgStart OutMsgIfBitClrArg
        ThreadProgCode <GetOpByteBX, GetWordArgBX, GetOpByte>
        ThreadProgCode <MovToCX, GetOpWord, SkipIfBitSetPtr, 1>
        ThreadProgCode <rprbuf>
ThreadProgEnd

;
; Outputs a forced lower case string if bit in pointer + offst is set.
; Use: OutMsgIfBitSetlc, offst, bitnumber, addr
;

ThreadProgStart OutMsgIfBitSetlc
        ThreadProgCode <GetOpByteBX, GetOpByte, MovToCX, GetOpWord>
        ThreadProgCode <SkipIfBitSetPtr, 1, EndProg, LowerCaseOn, rprbuf>
ThreadProgEnd

;155531
;
; Outputs a forced lower case string if bit in new pointer + offst is set.
; Use: OutMsgIfBitSetlc, addr, offst, bitnumber, addr1
;

ThreadProgStart OutMsgIfBitSetAddrlc
        ThreadProgCode <GetOpWordDI, GetOpByteBX, GetOpByte, MovToCX, GetOpWord>
        ThreadProgCode <SkipIfBitSetPtr, 1, EndProg, LowerCaseOn, rprbuf>
ThreadProgEnd

;155531
;
; Compare a specified word to a word pointed to by addr.
; If equal will change the opcode program flow.
; Use: JumpIfEQ, word, addr, opaddr.
;

ThreadProgStart JumpIfEQ
        ThreadProgCode <GetOpWord, SaveDword, GetOpWordBX, GetWordAdr, MovToBX>
        ThreadProgCode <RestDword, SkipIfeqBX, 2, GetOpWord, EndProg>
        ThreadProgCode <GetOpWord, MovToSI>
ThreadProgEnd

;
; Outputs a forced lower case string if bit in pointer + offst is clear.
; Use: OutMsgIfBitClrlc, offst, bitnumber, addr
;

ThreadProgStart OutMsgIfBitClrlc
        ThreadProgCode <GetOpByteBX, GetOpByte, MovToCX, GetOpWord>
        ThreadProgCode <SkipIfBitSetPtr, 2, LowerCaseOn, rprbuf>
ThreadProgEnd

;
; Outputs a forced lower case string if bit in pointer + argument is set.
; Use: OutMsgIfBitSetArglc, argnum, bitnumber, addr
;

ThreadProgStart OutMsgIfBitSetArglc
        ThreadProgCode <GetOpByteBX, GetWordArgBX, GetOpByte>
        ThreadProgCode <MovToCX, GetOpWord, SkipIfBitSetPtr, 1>
        ThreadProgCode <EndProg, LowerCaseOn, rprbuf>
ThreadProgEnd

;
; Outputs a forced lower case string if bit in pointer + argument is clear.
; Use: OutMsgIfBitClrArglc, argnum, bitnumber, addr
;

ThreadProgStart OutMsgIfBitClrArglc
        ThreadProgCode <GetOpByteBX, GetWordArgBX, GetOpByte>
        ThreadProgCode <MovToCX, GetOpWord, SkipIfBitSetPtr, 2>
        ThreadProgCode <LowerCaseOn, rprbuf>
ThreadProgEnd

;
; Pushes data on the stack.  Use: PushData, word.
;

ThreadProgStart PushData
        ThreadProgCode <GetOpWord, SaveDX>
ThreadProgEnd

;
; Pushes an argument from a call on the stack.  Use: PushArg, argnum.
;

ThreadProgStart PushArg
        ThreadProgCode <GetOpByteBX, GetWordArg, SaveDX>
ThreadProgEnd

;
; Loads the Cache with data from linear address at offst
; Use: LoadCache, offst
;

ThreadProgStart LoadCache
        ThreadProgCode <GetOpByteBX, Get32Ptr, LoadData, EndProg>
ThreadProgEnd

;
; Calls another OpCode format list saving the old list address.
; Use: CallOp, opaddr.
;

ThreadProgStart CallOp
        ThreadProgCode <GetOpWord, SaveSI, MovToSI>
ThreadProgEnd

;
; Changes the OpCode list pointer to the address.
; Use: JumpOp, opaddr.
;

ThreadProgStart JumpOp
        ThreadProgCode <GetOpWord, MovToSI>
ThreadProgEnd

;
; Output message from a table of addresses determined by the value
; of the bitfield.
; Use: OutMsgBitF, offst, startbit, bitwidth, word addr of addr table.
;

ThreadProgStart OutMsgBitF
        ThreadProgCode <GetOpByteBX, GetOpWord, MovToCX, GetBitFPtr>
        ThreadProgCode <MovToBX, AddToBX, GetOpWord, AddToBX>
        ThreadProgCode <GetWordAdr, rprbuf>
ThreadProgEnd

;
; Output message forced lower case from a table of addresses determined
; by the value of the bitfield.
; Use: OutMsgBitFlc, offst, startbit, bitwidth, word addr of addr table.
;

ThreadProgStart OutMsgBitFlc
        ThreadProgCode <GetOpByteBX, GetOpWord, MovToCX, GetBitFPtr>
        ThreadProgCode <MovToBX, AddToBX, GetOpWord, AddToBX>
        ThreadProgCode <GetWordAdr, LowerCaseOn, rprbuf>
ThreadProgEnd

;
; Changes the OpCode list pointer to the address if the bitfield at
; the offset is equal to the word value.
; Use: JumpIfBitF, offst, startbit, bitwidth, word value, opaddr.
;

ThreadProgStart JumpIfBitF
        ThreadProgCode <GetOpByteBX, GetOpWord, MovToCX, GetBitFPtr>
        ThreadProgCode <GetOpWordBX, SkipIfeqBX, 2, GetOpWord, EndProg>
        ThreadProgCode <GetOpWord, MovToSI>
ThreadProgEnd

;
; Jumps to the OpCode list pointer if the bit specified is set.
; Use: JumpIfBitSet, offst, bitnumber, opaddr.
;

ThreadProgStart JumpIfBitSet
        ThreadProgCode <GetOpByteBX, GetOpByte, MovToCX, GetOpWord>
        ThreadProgCode <SkipIfBitSetPtr, 1, EndProg, MovToSI>
ThreadProgEnd

;
; Jumps to the OpCode list pointer if the bit specified is clear.
; Use: JumpIfBitClr, offst, bitnumber, opaddr.
;

ThreadProgStart JumpIfBitClr
        ThreadProgCode <GetOpByteBX, GetOpByte, MovToCX, GetOpWord>
        ThreadProgCode <SkipIfBitSetPtr, 1, MovToSI>
ThreadProgEnd

;
; Jumps to the OpCode list pointer if the Dword at offset is null
; Use: JumpIfDwordNull, offst, opaddr
;

ThreadProgStart JumpIfDwordNull
        ThreadProgCode <GetOpByteBX, Get32Ptr, SkipIfNull32, 2>
        ThreadProgCode <GetOpWord, EndProg, GetOpWord, MovToSI>
ThreadProgEnd

;
; Changes the OpCode list pointer to the address if the INIT_ENV386
; bit is clear.
; Use: JumpIfEnv286, opaddr.
;

ThreadProgStart JumpIfEnv286
        ThreadProgCode <GetOpWord, SkipIfEnv386, 1, MovToSI>
ThreadProgEnd

;
; Changes the OpCode list pointer to the address if the INIT_ENV386
; bit is set.
; Use: JumpIfEnv386, opaddr.
;

ThreadProgStart JumpIfEnv386
        ThreadProgCode <GetOpWord, SkipIfEnv386, 1, EndProg, MovToSI>
ThreadProgEnd

;
; This opcode is a case statement. CaseJump defines the offset in
; the table and the bit field size and shape. The CaseEntry's after
; are compared until a match is found. If no match is found then
; the opcode after the last CaseEntry is executed.
;
; Use: CaseJump, offst, startbit, bitwidth
;      CaseEntry value, opaddr
;      CaseEnd                  ; last entry
;

ThreadProgStart CaseJump
        ThreadProgCode <GetOpByteBX, GetOpWord, MovToCX, GetBitFPtr, CaseCmp>
ThreadProgEnd

;
; Gets a string from the input buffer and jumps to the string's
; opcode address that matches.  The table that follows is made
; up of entries with the first word is the address of a string
; to match and a opcode list address to jump to if there is a
; match.  The list of these entries is terminated with a zero
; in the string address field.  This is also the default if
; there is no match.
; Use: InSwitchStr, addr of string and opcode addresses table.
;

ThreadProgStart InSwitchStr
        ThreadProgCode <GetInStr, SwitchStr>
ThreadProgEnd

;
; Get a byte from the input buffer and put it into the data
; table at the pointer.  Use: InBytePtr, offst.
;

ThreadProgStart InBytePtr
        ThreadProgCode <GetOpByteBX, SkipIfEOL, 2, EvalByte, PutBytePtr>
ThreadProgEnd

;
; Get a word from the input buffer and put it into the data
; table at the pointer.  Use: InWordPtr, offst.
;

ThreadProgStart InWordPtr
        ThreadProgCode <GetOpByteBX, SkipIfEOL, 2, EvalWord, PutWordPtr>
ThreadProgEnd

;
; Get a word from the input buffer and put it into the data
; table at the pointer + offst from argument list.
; Use: InWordArg, argnum.
;

ThreadProgStart InWordArg
        ThreadProgCode <GetOpByteBX, GetWordArgBX, SkipIfEOL, 2>
        ThreadProgCode <EvalWord, PutWordPtr>
ThreadProgEnd

;
; Get a 24 bit from the input buffer and put it into the data
; table at the pointer.  Use: In24bitArg, argnum.
;

ThreadProgStart In24bitArg
        ThreadProgCode <GetOpByteBX, GetWordArgBX, SkipIfEOL, 2>
        ThreadProgCode <EvalDword, Put24Ptr>
ThreadProgEnd

;
; Get a dword from the input buffer and put it into the data
; table at the pointer.  Use: InDwordArg, argnum.
;

ThreadProgStart InDwordArg
        ThreadProgCode <GetOpByteBX, GetWordArgBX, SkipIfEOL, 2>
        ThreadProgCode <EvalDword, Put32Ptr>
ThreadProgEnd

;
; Get data from the input buffer and put it into a bitfield
; in the data table at a offst for argument stack.
; Use: InBitFArg, argnum, startbit, bitwidth.
;

ThreadProgStart InBitFArg
        ThreadProgCode <GetOpByteBX, GetWordArgBX, GetOpWord, MovToCX>
        ThreadProgCode <SkipIfEOL, 2, EvalDword, PutBitFPtr>
ThreadProgEnd

;
; Unconditionally set a bit. Use: SetBitArg, argnum, bitnumber.
;

ThreadProgStart SetBitArg
        ThreadProgCode <GetOpByteBX, GetWordArgBX, GetOpByte>
        ThreadProgCode <MovToCX, SetBitPtr>
ThreadProgEnd

;
; Unconditionally clear a bit. Use: ClearBitArg, offst, bitnumber.
;

ThreadProgStart ClearBitArg
        ThreadProgCode <GetOpByteBX, GetWordArgBX, GetOpByte>
        ThreadProgCode <MovToCX, ClearBitPtr>
ThreadProgEnd

;
; Unconditionally toggle a bit. Use: ToggleBitArg, argnum, bitnumber.
;

ThreadProgStart ToggleBitArg
        ThreadProgCode <GetOpByteBX, GetWordArgBX, GetOpByte>
        ThreadProgCode <MovToCX, SkipIfBitSetPtr, 2, SetBitPtr>
        ThreadProgCode <EndProg, ClearBitPtr>
ThreadProgEnd

;
; Get a character from input buffer and compares it to the character
; in opcode.  If equal will change the opcode program flow and skip
; the character. Otherwise the character is still in the buffer.
; Use: JumpIfInCharEQ, char, opaddr.
;

ThreadProgStart JumpIfInCharEQ
        ThreadProgCode <GetOpByteBX, GetChr, ZeroExtendAL>
        ThreadProgCode <SkipIfeqBX, 2, GetOpWord, EndProg>
        ThreadProgCode <SkipB, GetOpWord, MovToSI>
ThreadProgEnd

;
; Checks if the current character in input buffer is EOL.
; Use: JumpIfEOL, opaddr.
;

ThreadProgStart JumpIfEOL
        ThreadProgCode <GetOpWord, SkipIfEOL, 1, EndProg, MovToSI>
ThreadProgEnd

;
; Get input from console. Use: GetInBuf.
;

ThreadProgStart GetInBuf
        ThreadProgCode <inbuf>
ThreadProgEnd

EndOpTables OpCodeTbl, ThreadProgTbl, ThreadAddrTbl

DataSegmentEnd

CodeSegmentStart


;***    OpInterpret - print a formatted data
;
;       Prints data in a format specified in the op code list.
;
;       ENTRY:  (SI) = address of format OpCode list.
;               (DI) = address of data.
;
;       EXIT:   NONE
;
;       USES:   AX, BX, CX, DX, SI, FLAGS
;

DefProc OpInterpret, NEAR, PUBLIC

LocVar  ThreadAddr, WORD                ; Address of thread routine
LocVar  ThreadPtr, WORD                 ; Current offset into thread program
LocVar  ReturnSave, WORD                ; Used to save return address
LocVar  TempSave, WORD                  ; Used to temporary register

BeginProc
        push    0                       ; put end of table mark on stack
GetOpCode:
        Pcall   GetOpByteBX
        shl     bx,1                    ; make word index.
        mov     bx,OpCodeTbl[bx]        ; get start of thread prog for opcode
        add     bx,dataOFFSET ThreadProgTbl
        mov     ThreadPtr,bx
GetNextThread:
        SaveReg <bx>                    ; don't trash bx in thread prog
        Pcall   GetThreadCode
        mov     bx,ThreadAddrTbl[bx]    ; get thread routine address
        mov     ThreadAddr,bx           ; save it
        RestReg <bx>
        call    ThreadAddr              ; call thread routine address
        jmp     short GetNextThread
EndProg:
        pop     bx                      ; pop off return address
        Pcall   LowerCaseOff            ; turn offset forced lower casing
        jmp     short GetOpCode
EndTable:
        xor     dx,dx                   ; no arguments
EndTableArg:
        pop     bx                      ; pop off return address
        pop     si                      ; pop off new format list
        or      si,si                   ; op code pointer and if
        je      short OpInterpExit      ;  zero then end of list.
        shl     dx,1
        add     sp,dx                   ; remove the arguments
        jmp     short GetOpCode
OpInterpExit:

EndProc


;***    OutOpString
;
;       Output a string from op prog table, incrementing the pointer.
;
;       ENTRY:  SI = opcode program pointer
;
;       EXIT:   NONE
;
;       USES:   AX, Flags
;

DefProc OutOpString, NEAR, LOCAL
oos10:  Pcall   GetOpByte
        mov     al,dl
        Pcall   put_ch
        or      al,al
        jnz     short oos10
EndProc


;***    PutStrPtr
;
;       Puts a string on console from table data pointer.
;
;       ENTRY:  DI = table data pointer
;               BX = offset into data table.
;               CX = maximum length of string.
;
;       EXIT:   NONE
;
;       USES:   AX, BX, Flags
;

DefProc PutStrPtr, NEAR, LOCAL
psp10:  mov     al,[di + bx]
        inc     bx
        Pcall   put_ch
        or      al,al
        loopnz  psp10
EndProc


;***    GetOpByte
;
;       Get byte from op prog table, extend and put in AX.
;
;       ENTRY:  SI = opcode program pointer
;
;       EXIT:   AX:DX = byte (zero extended) from op program table.
;               SI incremented.
;
;       USES:   AX, DX, SI, Flags
;

DefProc GetOpByte, NEAR, LOCAL
        lodsb
        jmp     short ZeroExtendAL
EndProc NoGen


;***    GetOpWord
;
;       Get word from op prog table put in AX.
;
;       ENTRY:  SI = opcode program pointer
;
;       EXIT:   AX:DX = word (zero extended) from op program table.
;               SI incremented.
;
;       USES:   AX, DX, SI, Flags
;

DefProc GetOpWord, NEAR, LOCAL
        lodsw
        jmp     short ZeroExtendAX
EndProc NoGen


;***    GetOpByteBX
;
;       Get byte from op prog table, extend and put in BX.
;
;       ENTRY:  SI = opcode program pointer
;
;       EXIT:   BX = byte (zero extended) from op program table.
;               SI incremented.
;
;       USES:   BX, Flags
;

DefProc GetOpByteBX, NEAR, LOCAL
        mov     bl,byte ptr [si]
        xor     bh,bh
        inc     si
EndProc


;***    GetOpWordBX
;
;       Get word from op prog table, extend and put in BX.
;
;       ENTRY:  SI = opcode program pointer
;
;       EXIT:   BX = word from op program table.
;               SI incremented.
;
;       USES:   BX, Flags
;

DefProc GetOpWordBX, NEAR, LOCAL
        mov     bx,[si]
        inc     si
        inc     si
EndProc

;155531
;***    GetDI
;
;       Gets DI from stack
;
;       ENTRY:  SP = pointer to DI save
;
;       EXIT:   NONE
;
;       USES:   SP
;

DefProc GetDI, NEAR, LOCAL
        pop     di
EndProc

;155531
;***    GetOpWordDI
;
;       Get word from op prog table, extend and put in DI.
;
;       ENTRY:  SI = opcode program pointer
;
;       EXIT:   DI = word from op program table.
;               SI incremented.
;
;       USES:   DI, Flags
;

DefProc GetOpWordDI, NEAR, LOCAL
        mov     di,[si]
        inc     si
        inc     si
EndProc

;***    GetWordAdr
;
;       Get word from [bx] and put in AX:DX.
;
;       ENTRY:  BX = address to get word from.
;
;       EXIT:   AX:DX = word from the address passed (zero extended).
;
;       USES:   AX, DX, Flags
;

DefProc GetWordAdr, NEAR, LOCAL
        xor     ax,ax
        mov     dx,[bx]
EndProc

;155531
;***    GetDwordAdr
;
;       Get Dword from [bx] and put in AX:DX.
;
;       ENTRY:  BX = address to get word from.
;
;       EXIT:   AX:DX = dword from the address passed.
;
;       USES:   AX, DX, Flags
;

DefProc GetDwordAdr, NEAR, LOCAL
        mov     ax,[bx].HighWord
        mov     dx,[bx].LowWord
EndProc

;***    GetWordArg
;
;       Get a word argument from stack with argument number in
;       BX and put in AX.
;
;       ENTRY:  BX = argument number.
;
;       EXIT:   AX:DX = argument word (zero extended).
;
;       USES:   AX, DX, BX, Flags
;

DefProc GetWordArg, NEAR, LOCAL
        shl     bx,1                    ; make argument number an word index
        add     bx,sp                   ; add stack pointer
        xor     ax,ax
        mov     dx,[bx + 4]             ; add 4 to skip return value and old SI
EndProc


;***    GetWordArgBX
;
;       Get a word argument from stack with argument number in
;       BX and put in BX.
;
;       ENTRY:  BX = argument number.
;
;       EXIT:   BX = argument word.
;
;       USES:   BX, Flags
;

DefProc GetWordArgBX, NEAR, LOCAL
        shl     bx,1                    ; make argument number an word index
        add     bx,sp                   ; add stack pointer
        mov     bx,[bx + 4]             ; add 4 to skip return value and old SI
EndProc


;***    ZeroExtendAL
;***    ZeroExtendAX
;
;       Zero extends AL or AX into AX:DX.
;
;       ENTRY:  AL or AX = input byte or word
;
;       EXIT:   AX:DX = input (zero extended).
;
;       USES:   AX, DX, Flags
;

DefProc ZeroExtendAL, NEAR, LOCAL
        xor     ah,ah

Entry   ZeroExtendAX, NEAR, LOCAL
        xor     dx,dx
        xchg    ax,dx
EndProc


;***    GetBitFPtr
;
;       Get and shift a bit field from table.
;
;       ENTRY:  DI = table data pointer
;               BX = offset into data table.
;               CL = starting bit number (0-31)
;               CH = bit field width (1-32)
;
;       EXIT:   AX:DX = shifted data.
;
;       USES:   AX, CX, DX, Flags
;

DefProc GetBitFPtr, NEAR, LOCAL
        Pcall   GetBytesPtr             ; (AX:DX) = data from table
        xchg    cl,ch                   ; (CL) = bit width, (CH) = start bit
        add     cl,ch                   ; (CL) = end bit number
        neg     cl
        and     cl,1fh                  ; (CL) = 31 - end bit number
        Pcall   shlaxdx                 ; (AX:DX) = shift right to clear hi bits
        add     cl,ch                   ; (CL) = right shift + start bit number
        Pcall   shraxdx                 ; (AX:DX) = shift left so start bit = 0
EndProc


;***    GetBytePtr
;
;       Get byte from [di + bx], extend and put in AX:DX.
;
;       ENTRY:  DI = table data pointer
;               BX = offset into data table.
;
;       EXIT:   AX:DX = byte (zero extended) from data table.
;
;       USES:   AX, DX, Flags
;

DefProc GetBytePtr, NEAR, LOCAL
        xor     ax,ax
        xor     dh,dh
        mov     dl,[di + bx]
EndProc


;***    GetWordPtr
;
;       Get word from [di + bx] put in AX:DX.
;
;       ENTRY:  DI = table data pointer
;               BX = offset into data table.
;
;       EXIT:   AX:DX = word from data table (zero extended).
;
;       USES:   AX, DX, Flags
;

DefProc GetWordPtr, NEAR, LOCAL
        xor     ax,ax
        mov     dx,[di + bx]
EndProc

;***    GetGateOffstPtr
;
;       Get 32 bit offset from gate descriptor at [di] put in AX:DX.
;
;       ENTRY:  DI = table data pointer
;
;       EXIT:   AX:DX = dword offset from gate descriptor.
;
;       USES:   AX, DX, Flags
;

DefProc GetGateOffstPtr, NEAR, LOCAL
        mov     dx,[di].GateOffset
        mov     ax,[di].GateExtOffset
EndProc


;***    GetDescLimitPtr
;
;       Get 32 bit limit from descriptor at [di] put in AX:DX.
;
;       ENTRY:  DI = table data pointer
;
;       EXIT:   AX:DX = dword limit from descriptor.
;
;       USES:   AX, DX, Flags
;

DefProc GetDescLimitPtr, NEAR, LOCAL
        xor     ah,ah
        mov     dx,[di].DescLimit
        mov     al,[di].DescAttr
        mov     ah,al
        and     al,D_EXTLIMIT
        test    ah,D_GRAN4K
        mov     ah,0
        jz      gdlx
        push    cx
        mov     cl,12                   ; (CL) = 12 (PAGESHIFT)
        Pcall   shlaxdx                 ; (AX:DX) >>= (CL)
        pop     cx
        or      dx,0fffh                ; 12 bits were zero filled
gdlx:
EndProc


;***    GetDescBasePtr
;
;       Get 32 bit base from descriptor at [di] put in AX:DX.
;
;       ENTRY:  DI = table data pointer
;
;       EXIT:   AX:DX = dword base from descriptor.
;
;       USES:   AX, DX, Flags
;

DefProc GetDescBasePtr, NEAR, LOCAL
        mov     dx,[di].DescBaseLow
        mov     al,[di].DescBaseHigh
        mov     ah,[di].DescBaseExt
EndProc


;***    Get24Ptr
;
;       Get 24 bits from [di + bx] put in AX:DX.
;
;       ENTRY:  DI = table data pointer
;               BX = offset into data table.
;
;       EXIT:   AX:DX = word from data table (zero extended).
;
;       USES:   AX, DX, Flags
;

DefProc Get24Ptr, NEAR, LOCAL
        xor     ah,ah
        mov     al,[di + bx].HighWord.LowByte
        mov     dx,[di + bx].LowWord
EndProc


;***    Get32Ptr
;
;       Get dword from [di + bx] put in AX:DX.
;
;       ENTRY:  DI = table data pointer
;               BX = offset into data table.
;
;       EXIT:   AX:DX = word from data table.
;
;       USES:   AX, DX, Flags
;

DefProc Get32Ptr, NEAR, LOCAL
        mov     ax,[di + bx].HighWord
        mov     dx,[di + bx].LowWord
EndProc


;***    PutBitFPtr
;
;       Put a bit field into data pointer
;
;       ENTRY:  AX:DX = data to shift into bit field.
;               DI = table data pointer.
;               BX = offset into data table.
;               CL = starting bit number (0-31)
;               CH = bit field width (1-32)
;
;       EXIT:   NONE
;
;       USES:   AX, CX, DX, Flags
;

DefProc PutBitFPtr, NEAR, LOCAL

LocVar  BitMask,DWORD
LocVar  Data,DWORD

BeginProc
        Pcall   shlaxdx                 ; shift data into starting bit pos
        mov     Data.HighWord,ax
        mov     Data.LowWord,dx
        Pcall   MakeMask                ; (AX:DX) = bitfield mask
        mov     BitMask.HighWord,ax
        mov     BitMask.LowWord,dx
        and     Data.HighWord,ax        ; mask the data to bitwidth size
        and     Data.LowWord,dx
        Pcall   GetBytesPtr             ; (AX:DX) = old bitfield data
        not     BitMask.HighWord        ; invert mask to clear bitfield
        not     BitMask.LowWord
        and     ax,BitMask.HighWord     ; clear the old bitfield
        and     dx,BitMask.LowWord
        or      ax,Data.HighWord        ; put new data into bitfield
        or      dx,Data.LowWord
        Pcall   PutBytesPtr
EndProc


;***    PutBytePtr
;
;       Puts a byte from AX:DX into [di + bx].
;
;       ENTRY:  DI = table data pointer.
;               BX = offset into table.
;               AX:DX = data to put into table (only DL is used).
;
;       EXIT:   NONE
;
;       USES:   NONE
;

DefProc PutBytePtr, NEAR, LOCAL
        mov     [di + bx],dl
EndProc


;***    PutWordPtr
;
;       Puts a word from AX:DX into [di + bx].
;
;       ENTRY:  DI = table data pointer.
;               BX = offset into table.
;               AX:DX = data to put into table (only DX is used).
;
;       EXIT:   NONE
;
;       USES:   NONE
;

DefProc PutWordPtr, NEAR, LOCAL
        mov     [di + bx],dx
EndProc


;***    Put24Ptr
;
;       Puts 24 bits from AX:DX into [di + bx].
;
;       ENTRY:  DI = table data pointer.
;               BX = offset into table.
;               AX:DX = data to put into table.
;
;       EXIT:   NONE
;
;       USES:   NONE
;

DefProc Put24Ptr, NEAR, LOCAL
        mov     [di + bx].HighWord.LowByte,al
        mov     [di + bx].LowWord,dx
EndProc


;***    Put32Ptr
;
;       Puts 32 bits from AX:DX into [di + bx].
;
;       ENTRY:  DI = table data pointer.
;               BX = offset into table.
;               AX:DX = data to put into table.
;
;       EXIT:   NONE
;
;       USES:   NONE
;

DefProc Put32Ptr, NEAR, LOCAL
        mov     [di + bx].HighWord,ax
        mov     [di + bx].LowWord,dx
EndProc


;***    SetBitPtr
;
;       Sets a bit.
;
;       ENTRY:  DI = table data pointer
;               BX = offset in data table.
;               CL = bit number (0-31).
;
;       EXIT:   NONE
;
;       USES:   AX, CX, DX, Flags
;
;

DefProc SetBitPtr, NEAR, LOCAL

LocVar  BitMask,DWORD

BeginProc
        mov     ch,1                    ; (CH) = bitwidth (1 bit)
        Pcall   MakeMask
        mov     BitMask.HighWord,ax
        mov     BitMask.LowWord,dx
        Pcall   GetBytesPtr
        or      ax,BitMask.HighWord
        or      dx,BitMask.LowWord
        Pcall   PutBytesPtr
EndProc


;***    ClearBitPtr
;
;       Clears a bit.
;
;       ENTRY:  DI = table data pointer
;               BX = offset in DI data table.
;               CL = bit number (0-31).
;
;       EXIT:   NONE
;
;       USES:   AX, CX, DX, Flags
;

DefProc ClearBitPtr, NEAR, LOCAL

LocVar  BitMask,DWORD

BeginProc
        mov     ch,1                    ; (CH) = bitwidth (1 bit)
        Pcall   MakeMask
        not     ax
        not     dx
        mov     BitMask.HighWord,ax
        mov     BitMask.LowWord,dx
        Pcall   GetBytesPtr
        and     ax,BitMask.HighWord
        and     dx,BitMask.LowWord
        Pcall   PutBytesPtr
EndProc


;***    SkipIfBitSetPtr
;
;       Skip n thread codes if bit set.
;
;       ENTRY:  DI = table data pointer.
;               BX = offset into table.
;               CL = bit number (0-31).
;               BP = OpInterpret's stack frame
;
;       EXIT:   NONE
;
;       USES:   Flags, [ThreadPtr]
;

DefProc SkipIfBitSetPtr, NEAR, LOCAL
        SaveReg <ax,bx,cx,dx>
        mov     ch,1                    ; (CL) = bit width
        Pcall   GetBytesPtr             ; (AX:DX) = data to test
        Pcall   GetThreadCode           ; (BX) = number of thread codes to skip
        inc     cl
        Pcall   shraxdx
        jnc     short sbs1              ; jump bit clear
        add     ThreadPtr,bx            ; skip them if bit is set
sbs1:   RestReg <dx,cx,bx,ax>
EndProc


;***    GetBytesPtr
;
;       Gets the number of bytes needed from table
;       for the bitfield passed.
;
;       ENTRY:  DI+BX = pointer into table.
;               CL = starting bit number (0-31)
;               CH = bit field width (1-32)
;
;       EXIT:   AX:DX = data from table (zero extended)
;
;       USES    AX, BX, CX, DX, Flags
;

DefProc GetBytesPtr, NEAR, LOCAL
        SaveReg <bx,cx>
        xor     ax,ax                   ; (AX:DX) = initial data value
        xor     dx,dx
        dec     ch                      ; (CH) = bit width (0-31)
        add     cl,ch                   ; (CL) = end bit number
        shr     cl,3                    ; (CL) = number of bytes - 1
        inc     cl                      ; (CL) = number of bytes to get
        xor     ch,ch                   ; (CX) = number of bytes to get
        jcxz    short gbs2
        add     bx,cx
gbs1:   mov     ah,al                   ; shift ax:dx left 8 bits
        mov     al,dh                   ;
        mov     dh,dl                   ;
        dec     bx
        mov     dl,[di + bx]
        loop    gbs1
gbs2:   RestReg <cx,bx>
EndProc


;***    PutBytesPtr
;
;       Puts the number of bytes needed from table
;       for the bitfield passed.
;
;       ENTRY:  DI+BX = pointer into table.
;               AX:DX = data to put into table
;               CL = starting bit number (0-31)
;               CH = bit field width (1-32)
;
;       EXIT:   NONE
;
;       USES    Flags
;

DefProc PutBytesPtr, NEAR, LOCAL
        SaveReg <ax,bx,cx,dx>
        dec     ch                      ; (CH) = bit width (0-31)
        add     cl,ch                   ; (CL) = end bit number
        shr     cl,3                    ; (CL) = number of bytes - 1
        inc     cl                      ; (CL) = number of bytes to get
        xor     ch,ch                   ; (CX) = number of bytes to get
        jcxz    short pbs2
pbs1:   mov     [di + bx],dl
        inc     bx
        mov     dl,dh                   ; shift ax:dx right 8 bits
        mov     dh,al                   ;
        mov     al,ah                   ;
        xor     ah,ah
        loop    pbs1
pbs2:   RestReg <dx,cx,bx,ax>
EndProc


;***    MakeMask
;
;       Makes a bit mask that corrosponds to the bitfield passed.
;
;       ENTRY:  CL = starting bit number (0-31)
;               CH = bit field width (1-32)
;
;       EXIT:   AX:DX = mask
;
;       USES:   AX, DX, Flags
;

DefProc MakeMask, NEAR, LOCAL
        push    cx
        xor     ax,ax                   ; (AX:DX) = initial mask
        xor     dx,dx
        mov     cl,ch                   ; (CL) = bit width
        xor     ch,ch                   ; (CX) = bit width
        jcxz    short mmk2
mmk1:   stc
        rcl     dx,1
        rcl     ax,1                    ; shift "bitwidth" number of set bits
        loop    mmk1                    ;  into mask
mmk2:   pop     cx                      ; (CL) = starting bit number
        Pcall   shlaxdx                 ; (AX:DX) = mask shifted into starting
EndProc                                 ;  starting bit position.

;***    MovToAX
;
;       Move dx to ax.
;
;       ENTRY:  DX = data to move to AX.
;
;       EXIT:   AX = DX
;
;       USES:   AX
;

DefProc MovToAX, NEAR, LOCAL
        mov     ax,dx
EndProc


;***    MovToBX
;
;       Move dx to bx.
;
;       ENTRY:  DX = data to move to BX.
;
;       EXIT:   BX = DX
;
;       USES:   BX
;

DefProc MovToBX, NEAR, LOCAL
        mov     bx,dx
EndProc


;***    MovToCX
;
;       Move dx to cx
;
;       ENTRY:  DX = data to move to CX.
;
;       EXIT:   CX = DX
;
;       USES:   CX
;
;

DefProc MovToCX, NEAR, LOCAL
        mov     cx,dx
EndProc


;***    MovToSI
;
;       Move dx to si.
;
;       ENTRY:  DX = data to move to SI.
;
;       EXIT:   SI = DX
;
;       USES:   SI
;

DefProc MovToSI, NEAR, LOCAL
        mov     si,dx
EndProc


;***    AddToBX
;
;       Add DX to BX.
;
;       ENTRY:  DX = data to add to BX.
;               BX = data to add DX to.
;
;       EXIT:   BX = BX + DX
;
;       USES:   BX, Flags
;

DefProc AddToBX, NEAR, LOCAL
        add     bx,dx
EndProc


;***    SaveAX
;
;       Saves AX.
;
;       ENTRY:  AX = data to save.
;               BP = OpInterpret's stack frame
;
;       EXIT:   NONE
;
;       USES:   [ReturnSave]
;

DefProc SaveAX, NEAR, LOCAL
        pop     ReturnSave
        push    ax
        push    ReturnSave
EndProc


;***    SaveDX
;
;       Saves DX on stack.
;
;       ENTRY:  DX = data to save.
;               BP = OpInterpret's stack frame
;
;       EXIT:   NONE
;
;       USES:   [ReturnSave]
;

DefProc SaveDX, NEAR, LOCAL
        pop     ReturnSave
        push    dx
        push    ReturnSave
EndProc


;***    SaveSI
;
;       Sets up for an opcode call.
;
;       ENTRY:  SI = data to save.
;               BP = OpInterpret's stack frame
;
;       EXIT:   NONE
;
;       USES:   [ReturnSave]
;

DefProc SaveSI, NEAR, LOCAL
        pop     ReturnSave
        push    si                      ; save old opcode program pointer
        push    ReturnSave
EndProc


;***    SaveImm
;
;       Push immediate value from ThreadCode table on to stack.
;
;       ENTRY:  BP = OpInterpret's stack frame
;
;       EXIT:   NONE
;
;       USES:   Flags, [ReturnSave], [TempSave]
;

DefProc SaveImm, NEAR, LOCAL
        mov     TempSave,bx
        Pcall   GetThreadCode           ; (BX) = byte from thread program
        pop     ReturnSave
        push    bx
        push    ReturnSave
        mov     bx,TempSave
EndProc

;155531
;***    SaveDword
;
;       Saves dword on stack.
;
;       ENTRY:  AX:DX = data to save.
;               BP = OpInterpret's stack frame
;
;       EXIT:   NONE
;
;       USES:   [ReturnSave]
;

DefProc SaveDword, NEAR, LOCAL
        pop     ReturnSave
        push    dx
        push    ax
        push    ReturnSave
EndProc

;***    RestDword
;
;       Restore dword.
;
;       ENTRY:  BP = OpInterpret's stack frame
;
;       EXIT:   NONE
;
;       USES:   [ReturnSave]
;

DefProc RestDword, NEAR, LOCAL
        pop     ReturnSave
        pop     ax
        pop     dx
        push    ReturnSave
EndProc

;***    CaseCmp
;
;       Compares AX:DX to each entry in a "CaseEntry" list
;       which is:
;
;               dd      value
;               dw      opaddr
;               ...
;               dd      value
;               dw      opaddr
;
;               dd      0               end of table
;               dw      0
;
;       ENTRY:  AX:DX = value to compare
;               SI = opcode program pointer.
;
;       EXIT:   (SI) = new opcode pointer
;
;       USES:   SI, Flags
;

DefProc CaseCmp, NEAR, LOCAL
ccp10:  cmp     word ptr [si].4,0       ; end of table?
        je      ccp30
        cmp     dx,[si].0               ; does low word of value match?
        jne     ccp20
        cmp     ax,[si].2               ; does high word of value match?
        jne     ccp20
        mov     si,[si].4               ; (SI) = new opcode pointer
        jmp     short ccpx

ccp20:  add     si,6                    ; next entry
        jmp     short ccp10

ccp30:  add     si,6                    ; skip CaseEnd
ccpx:
EndProc

;***    SkipIfeqBX
;
;       Skip n thread codes if AX is equal to DX.
;
;       ENTRY:  AX:DX = value to compare
;               BX = value to compare (zero extended to dword)
;               BP = OpInterpret's stack frame
;
;       EXIT:   NONE
;
;       USES:   Flags, [ThreadPtr]
;

DefProc SkipIfeqBX, NEAR, LOCAL
        SaveReg <bx,cx>
        mov     cx,bx
        Pcall   GetThreadCode           ; get number of thread codes to skip
        cmp     dx,cx                   ; compare DX and BX (which is in CX)
        jne     short sif1              ; don't do anything if equal
        or      ax,ax                   ; high word must be zero
        jnz     short sif1
        add     ThreadPtr,bx            ; skip them
sif1:   RestReg <cx,bx>
EndProc

;***    SkipIfNull32
;
;       Skip n thread codes if AX:DX is equal to NULL.
;
;       ENTRY:  AX:DX = value to compare
;               BX = value to compare (zero extended to dword)
;               BP = OpInterpret's stack frame
;
;       EXIT:   NONE
;
;       USES:   Flags, [ThreadPtr]
;

DefProc SkipIfNull32, NEAR, LOCAL
        SaveReg <bx,cx>
        Pcall   GetThreadCode           ; get number of thread codes to skip
        or      dx,dx                   ;
        jnz     short sin1              ; low word must be zero
        or      ax,ax                   ; high word must be zero
        jnz     short sin1
        add     ThreadPtr,bx            ; skip them
sin1:   RestReg <cx,bx>
EndProc


;***    SkipIfEOL
;
;       Skips a number of threads if there is no more
;       characters in input buffer.
;
;       ENTRY:  BP = OpInterpret's stack frame
;
;       EXIT:   NONE
;
;       USES:   Flags, [ThreadPtr]
;

DefProc SkipIfEOL, NEAR, LOCAL
        SaveReg <ax,bx>
        Pcall   GetThreadCode           ; get number of thread codes to skip
        Pcall   GetChr
        jnz     short sel1              ; jump if not EOL
        add     ThreadPtr,bx            ; skip them if bit is set
sel1:   RestReg <bx,ax>
EndProc

;***    SkipIfEnv386
;
;       Skips a number of threads if the INIT_ENV386
;       bit is set.
;
;       ENTRY:  BP = OpInterpret's stack frame
;
;       EXIT:   NONE
;
;       USES:   Flags, [ThreadPtr]
;

DefProc SkipIfEnv386, NEAR, LOCAL
        SaveReg <bx>
        Pcall   GetThreadCode           ; get number of thread codes to skip
        test    DebData.InitFlag,INIT_ENV386
        jz      short sie1              ; jump if bit not set
        add     ThreadPtr,bx            ; skip them if bit is set
sie1:   RestReg <bx>
EndProc

;***    GetInStr
;
;       Gets a string from the input buffer.
;
;       ENTRY:  NONE
;
;       EXIT:   (BX) = address of input buffer.
;               (CX) = length of inputed string.
;
;       USES:   AX, BX, CX, Flags
;

DefProc GetInStr, NEAR, PUBLIC
        xor     cx,cx                   ; clear character counter
        Pcall   ScanP
        mov     bx,pTXT                 ; get start of str after comma's
        jz      short gsl3
gsl1:   mov     al,byte ptr [bx]
        cmp     al,"0"                  ; allow "0" - "9"
        jb      short gsl3
        cmp     al,"9"
        jb      short gsl2
        cmp     al,"A"                  ; allow "A" - "Z"
        jb      short gsl3
        cmp     al,"Z"
        ja      short gsl3
gsl2:   inc     cx                      ; count character
        inc     bx                      ; next character
        jmp     short gsl1

gsl3:   xchg    bx,pTXT                 ; re-get start of string and
EndProc                                 ;  set new start.


;***    SwitchStr
;
;       Switch on a string in input buffer.
;
;       ENTRY:  (SI) = opcode program pointer.
;               (BX) = address of input string.
;               (CX) = length of input string.
;               if (CX) = zero, the default is used.
;
;       EXIT:   (SI) = new opcode program pointer.
;
;       USES:   AX, BX, CX, DX, SI, Flags
;

DefProc SwitchStr, NEAR, LOCAL
        mov     dx,4                    ; (DX) = table entry size
        Pcall   SearchString
        lodsw                           ; skip string address
        lodsw                           ; get new opcode address
        mov     si,ax                   ;  and make it the opcode list pointer
EndProc


;***    SearchString
;
;       Searchs a list of strings for the string passed. The search
;       is case insensitive.
;
;       ENTRY:  (SI) = pointer to string address table
;               (BX) = address of input string.
;               (CX) = length of input string.
;               if (CX) = zero, the default is used.
;               (DX) = table entry size
;
;       EXIT:   if carry clear,
;                       (SI) = matching entry
;               if carry set,
;                       (SI) = last one if no match
;
;       USES:   AX, BX, CX, SI, Flags
;

DefProc SearchString, NEAR, PUBLIC

sws0:   push    si
        lodsw
        or      ax,ax                   ; if string address = 0, end of table
        stc                             ; set carry for end of table
        jz      short sws4
        mov     si,ax                   ; setup pointer to table string
        push    bx
        push    cx

sws1:   lodsb
        cmp     al,"="                  ; end of string character
        je      short sws3
        or      al,al                   ; end of string character
        je      short sws3
        cmp     al," "                  ; end of string character
        je      short sws3
        jcxz    short sws2              ; no match, input string too short
        Pcall   upper                   ; (AL) = upper case
        mov     ah,al
        mov     al,[bx]
        Pcall   upper                   ; (AL) = upper case
        cmp     ah,al
        jne     short sws2              ; no match, try next entry
        inc     bx
        dec     cx
        jmp     short sws1

sws2:   pop     cx                      ; no match
        pop     bx
        pop     si
        add     si,dx                   ; next entry
        jmp     short sws0

sws3:   or      cx,cx                   ; found a match
        jne     short sws2              ; must be end of input string, too
        pop     cx
        pop     bx
        clc                             ; found entry
sws4:   pop     si

EndProc


;***    GetThreadCode
;
;       Get a thread code in BX from current thread program position and
;       increment current thread code pointer.
;
;       ENTRY:  BP = OpInterpret's stack frame
;
;       EXIT:   BX = thread code (zero extended).
;
;       USES:   BX, Flags, [ThreadPtr]
;

DefProc GetThreadCode, NEAR, LOCAL
        mov     bx,ThreadPtr            ; get current position in
        inc     ThreadPtr               ;  thread program.
        mov     bl,byte ptr [bx]
        xor     bh,bh
EndProc


;***    shlaxdx
;
;       Shift AX:DX left CL times, zero filling.
;
;       ENTRY:  AX:DX = data to shift
;               CL = number of times (0-32)
;
;       EXIT:   AX:DX = shifted data
;               Carry flag = last bit shift from the left
;
;       USES:   AX, DX, Flags
;

DefProc shlaxdx, NEAR, PUBLIC
        push    cx
        xor     ch,ch                   ; clears carry and CH
        jcxz    short shl2
shl1:   shl     dx,1
        rcl     ax,1
        loop    shl1
shl2:   pop     cx
EndProc


;***    shraxdx
;
;       Shift AX:DX right CL times, zero filling.
;
;       ENTRY:  AX:DX = data to shift
;               CL = number of times (0-32)
;
;       EXIT:   AX:DX = shifted data
;               Carry flag = last bit shift from the right
;
;       USES:   AX, DX, Flags
;

DefProc shraxdx, NEAR, PUBLIC
        push    cx
        xor     ch,ch                   ; clears carry and CH
        jcxz    short shr2
shr1:   shr     ax,1
        rcr     dx,1
        loop    shr1
shr2:   pop     cx
EndProc

;***    LoadData - Load the cache w/ new data
;
;       ENTRY:  AX:DX = address
;
;       EXIT:   Cache contains new data
;

DefProc LoadData, NEAR, LOCAL

LocVar  TmpAddr, <size AddrS>

BeginProc
        lea     bx,TmpAddr
        Pcall   FillLinAddr, <bx, dx, ax>       ; Get the address
        Pcall   CheckAddr, <bx>                 ; verify it
        mov     dx,MEMCACHESIZE
        Pcall   GetCache                        ; (DI) = cache buffer
        jnc     lld1
        Jump    ErrBreak
lld1:
EndProc

CodeSegmentEnd

        end
