    NAME SetsDoneRight

; This is Sets.Asm
; This file will replace the sets code from
; intel.  The basic assumption is that a set has a 
; of 0 and has <= 256 elements

; When on the stack, a set has the following format:
;
;        base  1 word
;        size  1 word
;        set   16 words

PUBLIC PQ_571, PQ_546, PQ_566, PQ_575, PQ_577, PQ_578
PUBLIC PQ_580, PQ_585, PQ_595

;CGROUP GROUP CODE

; constants

TRUE           EQU 1
FALSE          EQU 0
nullWord       EQU 0ffffh
nullByte       EQU 0ffh

setSizeInBits  EQU 256
setSizeInBytes EQU setSizeInBits/8
setSizeInWords EQU setSizeInBits/16
setStdBaseSize EQU 0
setHeaderSize  EQU 4
sizeOfLongReturn EQU 4

$EJ

SetType STRUC
    setBp DW ?
    setIp DW ?
    setCs DW ?
    setBase DW ?
    setSize DW ?
    setElements DW ?
    s2 DW ?
    s3 DW ?
    s4 DW ?
    s5 DW ?
    s6 DW ?
    s7 DW ?
    s8 DW ?
    s9 DW ?
    s10 DW ?
    s11 DW ?
    s12 DW ?
    s13 DW ?
    s14 DW ?
    s15 DW ?
    s16 DW ?
    setDestOff DW ?        ; or set in bytes
    setDestSeg DW ?
SetType ENDS

GRiDSets_CODE SEGMENT

             ASSUME CS:GRiDSets_CODE

valueOfZero DW 0           ; constant (in ram) used to push an empty set

$EJ

; MoveSet
;
; This will move a set from one place to another
; This does not use or assume anything on the stack
; It assumes the following:
;
;         DS:SI = source
;         ES:DI = destination
;         CX    = size of set in bits!!!

MoveSet PROC NEAR

    CMP CX, setSizeInBits     ; IF size > standardSize THEN
    JL MoveSetCont

    MOV CX, setSizeInBits     ;     size := standardSize

MoveSetCont:
    DEC CX                    ; size in bytes =
    SHR CX, 1                 ;    ((size in bits - 1) DIV 8) + 1
    SHR CX, 1
    SHR CX, 1                 ; convert from bits to bytes (CX := CX DIV 8)
    INC CX

    CLD
    REP MOVSB                 ; move set to destination

    RET
MoveSet ENDP

$EJ

; GetByte
;   
; This uses the set on the stack and BX to form
; a pointer to the byte which has the element (ES:DI)
; and an "oring" mask in AL
;
;    SS:SP -> ip
;       BP -> bp         ->     bp       BX = unchanged
;             ip                ip       ES:DI ^ proper byte in set
;             cs                cs       AX = (BX MOD 8) SHL (f7h)
;             base              base
;             size              size
;             set               set
;
; note: are sets stored 8 or 16 wide ???

GetByte PROC NEAR
    PUSH CX               ; save CX (might be a count)

    PUSH SS
    POP ES                ; ES := SS

    LEA DI, [BP].setElements   ; ES:DI ^ first byte in set

    MOV AX, BX
    SHR AX, 1
    SHR AX, 1
    SHR AX, 1             ; AX = element DIV 8

    ADD DI, AX            ; ES:DI ^ proper byte

    MOV CX, BX
    AND CX, 111b          ; CX := element MOD 8

    MOV AX, 00000001b
    ROL AX, CL            ; AX the right mask

    POP CX                ; restore CX
    RET
GetByte ENDP

$EJ

; PushEmptySet
;
; This will push an empty set onto the stack
;
;                         base      DX, CX changed
;                         size
;     SS:SP -> ip         set
;              ???        ???

PushEmptySet PROC NEAR

    POP DX                    ; save return address

    MOV CX, setSizeInWords    ; push this many 0 words

PushEmptyTopOfLoop:

    PUSH WORD PTR CS:valueOfZero ; push set on the stack

    LOOP PushEmptyTopOfLoop

    MOV CX, setSizeInBits     ; push standard set size
    PUSH CX

    MOV CX, setStdBaseSize    ; push standard set base (0)
    PUSH CX

    PUSH DX                   ; restore return address

    RET
PushEmptySet ENDP
    
$EJ

; PQ_ADD_MEM_STK (PQ_571)
;
; This will add a member to a set already on the stack
;
;
;     BX = member to set
;     SS:SP -> ip
;              cs
;              base     ->     base      
;              size            size
;              set             set       with the proper bit set

PQ_571 PROC FAR
    PUSH BP
    MOV BP, SP               ; initialize

    XOR BH, BH               ; normalize set

    CALL GetByte             ; do most of the work

    OR BYTE PTR ES:[DI],AL   ; set the bit

    POP BP                   ; unititialize
    RET
PQ_571 ENDP

$EJ

; PQ_ADD_RNG_STK (PQ_546)
;
; This will add a range of members to a set already on the stack
;
;
;     BX = member to start
;     AX = end of range
;     SS:SP -> ip
;              cs
;              base     ->     base      
;              size            size
;              set             set       with the proper bits set

PQ_546 PROC FAR
    PUSH BP
    MOV BP, SP               ; initialize

    XOR BH, BH               ; normalize set
    XOR AH, AH

    SUB AX, BX
    INC AX                   ; AX := (end - start) + 1
    MOV CX, AX               ; loop to AX times
    JCXZ PQ_546_Done         ; special case of zero

PQ_546_TopOfLoop:

    CALL GetByte             ; do most of the work

    OR BYTE PTR ES:[DI],AL   ; set the bit

    INC BX                   ; do next set member

    LOOP PQ_546_TopOfLoop

PQ_546_Done:

    POP BP                   ; unititialize
    RET
PQ_546 ENDP

$EJ

; PQ_IN_STK (PQ_566)
;
; This will check to see if a member is "IN" a set
; It also pops the set off of the stack
;
;    BX = member to check
;    SS:SP -> ip                  if found then AL = 1, ZF = 1
;             cs                  else          AL = 0, ZF = 0
;             base
;             size
;             set
;             ???         ->      ???


PQ_566 PROC FAR
    PUSH BP
    MOV BP, SP               ; initialize

    XOR BH, BH               ; normalize set

    CALL GetByte             ; do most of the work

    AND AL, BYTE PTR ES:[DI] ; is the bit set ?
    MOV AL, TRUE             ; IF yes THEN AL := 1
    JNZ PQ_566_Cleanup       

    MOV AL, FALSE            ; ELSE AL := 0

PQ_566_Cleanup:

    POP BP                   ; cleanup the stack

    POP SI                   ; save ip
    POP DI                   ; save cs

; Pop the set off of the set stack

    ADD SP, setSizeInBytes + setHeaderSize

    PUSH DI                  ; restore cs
    PUSH SI                  ; restore ip

    CMP AL, TRUE             ; set the ZF flag

    RET
PQ_566 ENDP

$EJ

; PQ_BLDST_EMPTY (PQ_575)
;
; This will just put an empty set onto the stack
;
;
;                   ->     base
;    SS:SP -> ip           size
;             cs           set       empty set
;             ???          ???

PQ_575 PROC FAR

    POP SI                 ; save the return address
    POP DI                 ;

    CALL PushEmptySet      ; push an empty set onto the stack

    PUSH DI                ; restore the return address
    PUSH SI

    RET
PQ_575 ENDP

$EJ

; PQ_BLDST_MEM (PQ_577)
;
; This will build a set on the stack and set 1 member
;
;     BX = member to set   -> base
;     SS:SP -> ip             size
;              cs             set     the proper member is set
;              ???            ???

PQ_577 PROC FAR

    XOR BH, BH            ; normalize set

    POP SI                ; save the return address
    POP DI

    CALL PushEmptySet     ; put empty set on the stack

    PUSH DI               ; restore the return address
    PUSH SI

    PUSH BP               ; initialize
    MOV BP, SP

    CALL GetByte          ; do most of the work

    OR BYTE PTR ES:[DI], AL ; set the bit

    POP BP                ; uninitialize
    RET
PQ_577 ENDP

$EJ

; PQ_BLDST_RNG (PQ_578)
;
; This is just like PQ_BLDST_MEM except a range is set
;
;     AX = end of range
;     BX = member to start -> base
;     SS:SP -> ip             size
;              cs             set     the proper members are set
;              ???            ???

PQ_578 PROC FAR

    XOR AH, AH            ; normalize set
    XOR BH, BH

    POP SI                ; save the return address
    POP DI

    CALL PushEmptySet     ; put empty set on the stack

    PUSH DI               ; restore the return address
    PUSH SI

    PUSH BP               ; initialize
    MOV BP, SP

    SUB AX, BX
    INC AX                ; AX := (end - start) + 1
    MOV CX, AX            ; loop AX times
    JCXZ PQ_578_Done

PQ_578_TopOfLoop:

    CALL GetByte          ; do most of the work

    OR BYTE PTR ES:[DI], AL ; set the bit

    INC BX                ; do the next member

    LOOP PQ_578_TopOfLoop

PQ_578_Done:

    POP BP                ; uninitialize
    RET
PQ_578 ENDP

$EJ

; PQ_PUSH_SET (PQ_580)
;
; This will push a set from a variable on to the stack
;
;    CX = size
;    DX = base
;    SS:SP -> ip        ->     base
;             cs               size
;             ^source          set
;             ???              ???
;
; note: This works only for standard sized sets

PQ_580 PROC FAR

    POP SI                ; save return address
    POP DI

    POP AX                ; save ^source
    POP BX

    MOV ES, CX            ; save size

    CALL PushEmptySet     ; push blank set onto stack
                          ; CX, DX get stomped

    MOV CX, ES            ; restore size

    PUSH DI               ; restore return address
    PUSH SI

    PUSH BP               ; initialize
    MOV BP, SP
    PUSH DS

    MOV SI, AX            ; set up source pointer
    MOV DS, BX

    LEA DI, [BP].setElements ; set up destination pointer
    PUSH SS
    POP ES

    CALL MoveSet          ; move the set

    POP DS                ; uninitialize
    POP BP
    RET
PQ_580 ENDP

$EJ

; PQ_POP_SET (PQ_585)
; 
; This will pop a set from the stack into a variable
;
;
;    DX = base
;    CX = size
;    SS:SP -> ip
;             cs
;             base
;             size
;             set
;             ^destination
;             ???            ->   ???
;
; note: This works only for standard sized sets


PQ_585 PROC FAR
    PUSH BP                   ; initialize
    MOV BP, SP
    PUSH DS

    MOV DI, [BP].setDestOff   ; build destination pointer
    MOV AX, [BP].setDestSeg
    MOV ES, AX

    MOV AX, setSizeInBytes + setHeaderSize + sizeOfLongReturn

; AX = # bytes to pop off the stack when done
; This label is also entered by the previous procedure
; to save code
; It assumes that CX = size of the set

PQ_Finish_The_Job:

    LEA SI, [BP].setElements  ; build source pointer
    PUSH SS
    POP DS

    CALL MoveSet

    POP DS                    ; uninitialize
    POP BP

    POP SI                    ; save return address
    POP DI

; Pop the set off of the set stack

    ADD SP, AX                ; from up there

    PUSH DI                   ; restore return address
    PUSH SI

    RET
PQ_585 ENDP

$EJ

; PQ_UTS_TO_ARG (PQ_595)
;
; This will convert a set from "stack set" format to just
; an array of words with no size or base
;
;     DX = base
;     CX = size
;     SS:SP -> ip
;              cs
;              base
;              size
;              set
;              set in bytes  ->   set in bytes filled in
;              ???                ???

PQ_595 PROC FAR
    PUSH BP              ; initialize
    MOV BP, SP
    PUSH DS

    PUSH SS              ; set up destination pointer
    POP ES
    LEA DI, [BP].setDestOff   ; this label is misleading

    MOV AX, setSizeInBytes + setHeaderSize

; The rest of this routine is the same as
; the end of the previous routine

    JMP PQ_Finish_The_Job

PQ_595 ENDP

GRiDSets_CODE ENDS

  END
