www.pudn.com > 汇编源代码大全.rar > MODEX.ASM, change:1993-03-30,size:56994b


; 
; MODEX.ASM - A Complete Mode X Library  
;  
; By Matt Pritchard  
; With considerable input from Michael Abrash  
;  
; The following information is donated to the public domain in  
; the hopes that save other programmers much frustration.  
;  
; If you do use this code in a product, it would be nice if  
; you include a line like 'Mode X routine by Matt Pritchard'  
; in the credits. ' 
; 
 
    .MODEL Medium, Basic 
    .286 
    PAGE    255, 132 
 
    ; ==== MACROS ==== 
 
OUT_16 MACRO Register, Value 
    MOV     DX, Register        ; Select Register 
    MOV     AX, Value           ; Get Out Value 
    OUT     DX, AX              ; Set Register(s)  
    ENDM 
 
OUT_8 MACRO Register, Value 
    MOV     DX, Register        ; Select Register 
    MOV     AL, Value           ; Get Out Value 
    OUT     DX, AL              ; Set Register  
    ENDM 
 
    ; ==== General Constants ==== 
 
    False   EQU 0 
    True    EQU -1 
 
    b       EQU BYTE PTR 
    w       EQU WORD PTR 
    d       EQU DWORD PTR 
 
    ; ==== VGA Register Values ==== 
 
    VGA_Segment     EQU 0A000H  ; Vga Memory Segment 
 
    ATTRIB_Ctrl     EQU 03C0H   ; VGA Attribute Controller 
    GC_Index        EQU 03CEH   ; VGA Graphics Controller 
    SC_Index        EQU 03C4H   ; VGA Sequencer Controller 
    SC_Data         EQU 03C5H   ; VGA Sequencer Data Port 
    CRTC_Index      EQU 03D4H   ; VGA CRT Controller 
    CRTC_Data       EQU 03D5H   ; VGA CRT Controller Data 
    MISC_OUTPUT     EQU 03C2H   ; VGA Misc Register 
    INPUT_1         EQU 03DAH   ; Input Status #1 Register 
 
    DAC_WRITE_ADDR  EQU 03C8H   ; VGA DAC Write Addr Register 
    DAC_READ_ADDR   EQU 03C7H   ; VGA DAC Read Addr Register 
    PEL_DATA_REG    EQU 03C9H   ; VGA DAC/PEL data Register R/W 
 
    MAP_MASK        EQU 02      ; Map Register # 
    READ_MAP        EQU 04      ; Read Map Register # 
    MAP_MASK_PLANE1 EQU 0102h   ; Map Register + Plane 1 
 
    START_DISP_HI   EQU 0Ch     ; CRTC Index: Display Start Addr Hi 
    START_DISP_LO   EQU 0Dh     ; CRTC Index: Display Start Addr Lo 
 
    PLANE_BITS      EQU 03h     ; Bits 0-1 of Xpos = Plane # 
    ALL_PLANES      EQU 0Fh     ; All Bit Planes Selected 
    ALL_PLANES_ON   EQU 0F02h   ; Map Register + All Bit Planes 
    CHAIN4_OFF      EQU 0604h   ; Chain 4 mode Off 
    ASYNC_RESET     EQU 0100h   ; (A)synchronous Reset 
    SEQU_RESTART    EQU 0300h   ; Sequencer Restart 
 
    ; Constants Specific for these routines 
 
    NUM_MODES       EQU 8       ; # of Mode X Variations 
 
    ; Specific Mode Data Table format... 
 
    M_MiscR         EQU 0       ; Value of MISC_OUTPUT register 
    M_Pages         EQU 1       ; Maximum Possible # of pages 
    M_XSize         EQU 2       ; X Size Displayed on screen 
    M_YSize         EQU 4       ; Y Size Displayed on screen 
    M_XMax          EQU 6       ; Maximum Possible X Size 
    M_YMax          EQU 8       ; Maximum Possible Y Size 
    M_CRTC          EQU 10      ; Table of CRTC register values 
 
    ; ==== DGROUP STORAGE NEEDED (36 BYTES) ===== 
 
    .DATA? 
 
SCREEN_WIDTH    DW  0           ; Width of a line in Bytes  
SCREEN_WIDTHx4  DW  0           ; Width of a line in Pixels  
SCREEN_HEIGHT   DW  0           ; Veritcal Height in Pixels 
 
LAST_PAGE       DW  0           ; # of Display Pages  
PAGE_ADDR       DW  0, 0, 0, 0  ; Offsets to start of each page 
 
PAGE_SIZE       DW  0           ; Size of Page in Addr Bytes 
 
DISPLAY_PAGE    DW  0           ; Page # currently displayed  
ACTIVE_PAGE     DW  0           ; Page # currently active 
 
CURRENT_PAGE    DW  0           ; Offset of current Page  
CURRENT_SEGMENT DW  0           ; Segment of VGA memory 
 
CURRENT_XOFFSET DW  0           ; Current Display X Offset  
CURRENT_YOFFSET DW  0           ; Current Display Y Offset 
 
CURRENT_MOFFSET DW  0           ; Current Start Offset 
 
MAX_XOFFSET     DW  0           ; Current Display X Offset  
MAX_YOFFSET     DW  0           ; Current Display Y Offset 
 
    .CODE 
 
    ; Data Tables, Put in Code Segement for Easy Access 
    ; (Like when all the other Segment Registers are in 
    ; use!!) and reduced DGROUP requirements... 
 
LEFT_CLIP_MASK      DB  0FH, 0EH, 0CH, 08H 
 
RIGHT_CLIP_MASK     DB  01H, 03H, 07H, 0FH 
 
MODE_TABLE: 
        DW  OFFSET MODE_320x200, OFFSET MODE_320x400 
        DW  OFFSET MODE_360x200, OFFSET MODE_360x400 
        DW  OFFSET MODE_320x240, OFFSET MODE_320x480 
        DW  OFFSET MODE_360x240, OFFSET MODE_360x480 
 
MODE_320x200:           ; Data for 320 by 200 Pixels 
 
        DB  063h        ; 400 scan Lines & 25 Mhz Clock 
        DB  4           ; Maximum of 4 Pages 
        DW  320, 200    ; Displayed Pixels (X,Y) 
        DW  1302, 816   ; Max Possible X and Y Sizes 
 
        ;CRTC Setup Data.... 
 
        DW  00014H      ; Dword Mode off 
        DW  0E317H      ; turn on Byte Mode 
        DW  00000h      ; End of CRTC Data for 320x200 
 
MODE_320x400:           ; Data for 320 by 400 Pixels 
 
        DB  063h        ; 400 scan Lines & 25 Mhz Clock 
        DB  2           ; Maximum of 2 Pages 
        DW  320, 400    ; Displayed Pixels X,Y 
        DW  648, 816    ; Max Possible X and Y Sizes 
 
        ;CRTC Setup Data.... 
 
        DW  04009H      ; Cell Heigth (1 Scan Line) 
        DW  00014H      ; Dword Mode off 
        DW  0E317H      ; turn on Byte Mode 
        DW  00000h      ; End of CRTC Data for 320x400 
 
MODE_360x240:           ; Data for 360 by 240 Pixels 
 
        DB  0E7h        ; 480 scan Lines & 28 Mhz Clock 
        DB  3           ; Maximum of 3 Pages 
        DW  360, 240    ; Displayed Pixels X,Y 
        DW  1092, 728   ; Max Possible X and Y Sizes 
 
        ;CRTC Setup Data.... 
 
        DW  06B00H      ; Horz total 
        DW  05901H      ; Horz Displayed 
        DW  05A02H      ; Start Horz Blanking 
        DW  08E03H      ; End Horz Blanking 
        DW  05E04H      ; Start H Sync 
        DW  08A05H      ; End H Sync 
        DW  00D06H      ; Vertical Total 
        DW  03E07H      ; Overflow 
        DW  04109H      ; Cell Heigth (2 Scan Lines) 
        DW  0EA10H      ; V Sync Start 
        DW  0AC11H      ; V Sync End/Prot Cr0 Cr7 
        DW  0DF12H      ; Vertical Displayed 
        DW  00014H      ; Dword Mode off 
        DW  0E715H      ; V Blank Start 
        DW  00616H      ; V Blank End 
        DW  0E317H      ; turn on Byte Mode 
        DW  00000h      ; End of CRTC Data for 360x240 
 
MODE_360x480:           ; Data for 360 by 480 Pixels 
 
        DB  0E7h        ; 480 scan Lines & 28 Mhz Clock 
        DB  1           ; Only 1 Page Possible 
        DW  360, 480    ; Displayed Pixels X,Y 
        DW  544, 728    ; Max Possible X and Y Sizes 
 
        ;CRTC Setup Data.... 
 
        DW  06B00H      ; Horz total 
        DW  05901H      ; Horz Displayed 
        DW  05A02H      ; Start Horz Blanking 
        DW  08E03H      ; End Horz Blanking 
        DW  05E04H      ; Start H Sync 
        DW  08A05H      ; End H Sync 
        DW  00D06H      ; Vertical Total 
        DW  03E07H      ; Overflow 
        DW  04009H      ; Cell Heigth (1 Scan Line) 
        DW  0EA10H      ; V Sync Start 
        DW  0AC11H      ; V Sync End/Prot Cr0 Cr7 
        DW  0DF12H      ; Vertical Displayed 
        DW  00014H      ; Dword Mode off 
        DW  0E715H      ; V Blank Start 
        DW  00616H      ; V Blank End 
        DW  0E317H      ; turn on Byte Mode 
 
        DW  00000h      ; End of CRTC Data for 360x480 
 
MODE_320x240:           ; Data for 320 by 240 Pixels 
 
        DB  0E3h        ; 480 scan Lines & 25 Mhz Clock 
        DB  3           ; Maximum of 3 Pages 
        DW  320, 240    ; Displayed Pixels X,Y 
        DW  1088, 818   ; Max Possible X and Y Sizes 
 
        DW  00D06H      ; Vertical Total 
        DW  03E07H      ; Overflow 
        DW  04109H      ; Cell Heigth (2 Scan Lines) 
        DW  0EA10H      ; V Sync Start 
        DW  0AC11H      ; V Sync End/Prot Cr0 Cr7 
        DW  0DF12H      ; Vertical Displayed 
        DW  00014H      ; Dword Mode off 
        DW  0E715H      ; V Blank Start 
        DW  00616H      ; V Blank End 
        DW  0E317H      ; turn on Byte Mode 
 
        DW  00000h      ; End of CRTC Data for 320x240 
 
MODE_320x480:           ; Data for 320 by 480 Pixels 
 
        DB  0E3h        ; 480 scan Lines & 25 Mhz Clock 
        DB  1           ; Only 1 Page Possible 
        DW  320, 480    ; Displayed Pixels X,Y 
        DW  540, 818    ; Max Possible X and Y Sizes 
 
        DW  00D06H      ; Vertical Total 
        DW  03E07H      ; Overflow 
        DW  04009H      ; Cell Heigth (1 Scan Line) 
        DW  0EA10H      ; V Sync Start 
        DW  0AC11H      ; V Sync End/Prot Cr0 Cr7 
        DW  0DF12H      ; Vertical Displayed 
        DW  00014H      ; Dword Mode off 
        DW  0E715H      ; V Blank Start 
        DW  00616H      ; V Blank End 
        DW  0E317H      ; turn on Byte Mode 
 
        DW  00000h      ; End of CRTC Data for 320x480 
 
MODE_360x200:           ; Data for 360 by 200 Pixels 
 
        DB  067h        ; 400 scan Lines & 28 Mhz Clock 
        DB  3           ; Maximum of 3 Pages 
        DW  360, 200    ; Displayed Pixels (X,Y) 
        DW  1302, 728   ; Max Possible X and Y Sizes 
 
        ;CRTC Setup Data.... 
 
        DW  06B00H      ; Horz total 
        DW  05901H      ; Horz Displayed 
        DW  05A02H      ; Start Horz Blanking 
        DW  08E03H      ; End Horz Blanking 
        DW  05E04H      ; Start H Sync 
        DW  08A05H      ; End H Sync 
        DW  00014H      ; Dword Mode off 
        DW  0E317H      ; turn on Byte Mode 
        DW  00000h      ; End of CRTC Data for 360x200 
 
MODE_360x400:           ; Data for 360 by 400 Pixels 
 
        DB  067h        ; 400 scan Lines & 28 Mhz Clock 
        DB  1           ; Maximum of 1 Pages 
        DW  360, 400    ; Displayed Pixels X,Y 
        DW  648, 816    ; Max Possible X and Y Sizes 
 
        ;CRTC Setup Data.... 
 
        DW  06B00H      ; Horz total 
        DW  05901H      ; Horz Displayed 
        DW  05A02H      ; Start Horz Blanking 
        DW  08E03H      ; End Horz Blanking 
        DW  05E04H      ; Start H Sync 
        DW  08A05H      ; End H Sync 
        DW  04009H      ; Cell Heigth (1 Scan Line) 
        DW  00014H      ; Dword Mode off 
        DW  0E317H      ; turn on Byte Mode 
        DW  00000h      ; End of CRTC Data for 360x400 
 
 
; ===== ACTUAL ASSEMBLY CODE ===== 
 
;======================================================  
;SET_VGA_MODEX%(ModeType%, MaxXPos%, MaxYpos%, Pages%) 
;======================================================  
;  
; Sets Up the specified version of Mode X.  Allows for  
; the setup of multiple video pages, and a virtual  
; screen which can be larger than the displayed screen  
; (which can then be scrolled a pixel at a time)  
;  
; INPUT PARAMETERS:  
;  
; ModeType%  - The Desired Version of 256 Color Mode X  
;  
;     0 =  320 x 200, 4 Pages max,  1.2:1 Aspect Ratio  
;     1 =  320 x 400, 2 Pages max,  2.4:1 Aspect Ratio  
;     2 =  360 x 200, 3 Pages max,  1.35:1 Aspect Ratio  
;     3 =  360 x 400, 1 Page  max,  2.7:1 Aspect Ratio  
;     4 =  320 x 240, 3 Pages max,  1:1 Aspect Ratio  
;     5 =  320 x 480, 1 Page  max,  2:1 Aspect Ratio  
;     6 =  360 x 240, 3 Pages max,  1.125:1 Aspect Ratio  
;     7 =  360 x 480, 1 Page  max,  2.25:1 Aspect Ratio  
;  
; MaxXpos = The Desired Virtual Screen Width  
; MaxYpos = The Desired Virtual Screen Height  
; Pages%  = The Desired # of Video Pages  
;  
; RETURNS in AX:    0 (= Failure) or ffff (= Success)  
; 
 
SVM_STACK   STRUC 
                DW  ?,?,?,? ;DI,SI,DS,BP 
                DD  ?       ;Caller 
    SVM_Pages   DW  ? 
    SVM_Ysize   DW  ? 
    SVM_Xsize   DW  ? 
    SVM_Mode    DW  ? 
SVM_STACK   ENDS 
 
    PUBLIC  SET_VGA_MODEX 
 
SET_VGA_MODEX   PROC    FAR 
 
    PUSH    BP                  ; Preserve Important 
    PUSH    DS                  ; Register Values 
    PUSH    SI 
    PUSH    DI 
 
    MOV     BP,SP               ; Set up Stack Frame 
 
    ;Check Legality of Mode Request.... 
 
    MOV     BX, [BP].SVM_Mode   ; Get Requested Mode # 
    CMP     BX, NUM_MODES       ; Is it 0..7? 
    JAE     SVM_BadModeSetup    ; If Not, Error out 
 
    SHL     BX, 1                   ; Scale BX 
    MOV     SI, w CS:MODE_TABLE[BX] ; CS:SI -> Mode Info 
    PUSH    SI                      ; Save for later use 
 
    ;Check # of Requested Display Pages 
 
    MOV     CX, [Bp].SVM_Pages  ; Get # of Requested Pages 
    CMP     CX, CS:[SI].M_Pages ; Check # Pages for mode 
    JA      SVM_BadModeSetup    ; Report Error if too Many 
    OR      CX, CX              ; # Pages = 0? 
    JE      SVM_BadModeSetup    ; Report Error if 0 
 
    ;Check Validity of X Size 
 
    AND     [BP].SVM_XSize, 0FFF8h  ;X size Mod 8 Must = 0 
 
    MOV     AX, [BP].SVM_XSize  ; Get Logical Screen Width 
    CMP     AX, CS:[SI].M_XSize ; Check against Displayed X 
    JB      SVM_BadModeSetup    ; Report Error if too small 
    CMP     AX, CS:[SI].M_XMax  ; Check against Max X 
    JA      SVM_BadModeSetup    ; Report Error if too big 
 
    ;Check Validity of Y Size 
 
    MOV     BX, [BP].SVM_YSize  ; Get Logical Screen Height 
    CMP     BX, CS:[SI].M_YSize ; Check against Displayed Y 
    JB      SVM_BadModeSetup    ; Report Error if too small 
    CMP     BX, CS:[SI].M_YMax  ; Check against Max Y 
    JA      SVM_BadModeSetup    ; Report Error if too big 
 
    ;Enough memory to Fit it all? 
 
    SHR     AX, 2               ;# of Bytes:Line = XSize/4 
    MUL     BX                  ;# Bytes/Page = AX/4 * BX 
    JO      SVM_BadModeSetup    ;Exit if Page Size > 256K 
 
    MUL     CX                  ;Total Mem = PageSize * Pages 
    JNO     SVM_Continue        ;Exit if Total Size > 256K 
 
SVM_BadModeSetup: 
 
    XOR     AX, AX              ; Return Value = False 
    JMP     SVM_Exit            ; Normal Exit 
 
SVM_Continue: 
 
    MOV     AX, 13H             ; Start with Mode 13H 
    INT     10H                 ; Let BIOS Set Mode 
 
    OUT_16  SC_INDEX, CHAIN4_OFF    ; Disable Chain 4 Mode 
    OUT_16  SC_INDEX, ASYNC_RESET   ; (A)synchronous Reset 
 
    MOV     DX, MISC_OUTPUT     ; VGA Misc Register 
    MOV     AL, CS:[SI].M_MiscR ; Get New Timing/Size Value 
    OUT     DX, AL              ; Set VGA Misc Register 
 
    OUT_16  SC_INDEX, SEQU_RESTART  ; Restart Sequencer ... 
 
    MOV     DX, CRTC_INDEX      ; Vga crtc Registers 
    MOV     AL, 11H             ; Vert Retrace End Register 
    OUT     DX, AL              ; Load Current Value 
    INC     DX                  ; Point to Data 
    IN      AL, DX              ; Get Value, Bit 7 = Protect 
    AND     AL, 7FH             ; Mask out Write Protect 
    OUT     DX, AL              ; And send it back 
 
    MOV     DX, CRTC_INDEX      ; Vga Crtc Registers 
    ADD     SI, M_CRTC          ; SI -> CRTC Parameter Data 
 
SVM_Setup_CRTC: 
 
    MOV     AX, CS:[SI]         ; Get CRTC Data from Table 
    ADD     SI, 2               ; Advance Pointer 
    OR      AX, AX              ; At End of Data Table? 
    JZ      SVM_Set_Data        ; If so, Exit Loop 
 
    OUT     DX, AX              ; Reprogram VGA CRTC reg 
    JMP     SVM_Setup_CRTC      ; Process Next Table Entry 
 
SVM_Set_Data: 
 
    XOR     AX, AX              ; AX = 0 
    MOV     DISPLAY_PAGE, AX    ; Display Page = 0 
    MOV     ACTIVE_PAGE, AX     ; Active Page = 0 
    MOV     CURRENT_PAGE, AX    ; Current Page (Offset) = 0 
    MOV     CURRENT_XOFFSET, AX ; Horz Scroll Index = 0 
    MOV     CURRENT_YOFFSET, AX ; Vert Scroll Index = 0 
    MOV     CURRENT_MOFFSET, AX ; Memory Scroll Index = 0 
 
    MOV     AX, VGA_SEGMENT     ; Segment for VGA memory 
    MOV     CURRENT_SEGMENT, AX ; Save for Future LES's 
 
    ;Set Logical Screen Width, X Scroll and Our Data 
 
    POP     SI                  ; Get Saved Ptr to Mode Info 
    MOV     AX, [Bp].SVM_Xsize  ; Get Display Width 
 
    MOV     CX, AX              ; CX = Logical Width 
    SUB     CX, CS:[SI].M_XSize ; CX = Max X Scroll Value 
    MOV     MAX_XOFFSET, CX     ; Set Maximum X Scroll 
 
    MOV     SCREEN_WIDTHx4, AX  ; Save Width in Pixels 
    SHR     AX, 2               ; Bytes = Pixels / 4 
    MOV     SCREEN_WIDTH, AX    ; Save Width in Pixels 
 
    SHR     AX, 1               ; Offset Value = Bytes / 2 
    MOV     AH, 13h             ; CRTC Offset Register Index 
    XCHG    AL, AH              ; Switch format for OUT 
    OUT     DX, AX              ; Set VGA CRTC Offset Reg 
 
    ;Setup Data table, Y Scroll, Misc for Other Routines 
 
    MOV     AX, [Bp].SVM_Ysize  ; Get Logical Screen Hieght 
 
    MOV     CX, AX              ; CX = Logical Height 
    SUB     BX, CS:[SI].M_YSize ; CX = Max Y Scroll Value 
    MOV     MAX_YOFFSET, CX     ; Set Maximum Y Scroll 
 
    MOV     SCREEN_HEIGHT, AX   ; Save Hight in Pixels 
    MUL     SCREEN_WIDTH        ; AX = Page Size in Bytes, 
    MOV     PAGE_SIZE, AX       ; Save Page Size 
 
    MOV     CX, [Bp].SVM_Pages  ; Get # of Pages 
    MOV     LAST_PAGE, CX       ; Save # of Pages 
 
    MOV     BX, 0               ; Page # = 0 
    MOV     DX, BX              ; Page 0 Offset = 0 
 
SVM_Set_Pages: 
 
    MOV     PAGE_ADDR[BX], DX   ; Set Page #(BX) Offset 
    ADD     BX, 2               ; Page#++ 
    ADD     DX, AX              ; Compute Addr of Next Page 
    LOOP    SVM_Set_Pages       ; Loop until Done 
 
    ;Clear VGA Memory 
 
    OUT_16  SC_INDEX, ALL_PLANES_ON ; Select All Planes 
    LES     DI, d CURRENT_PAGE      ; Point to Start of VGA memory 
 
    XOR     AX, AX              ; AX = 0 
    CLD                         ; Block Xfer Forwards 
    MOV     CX, 8000H           ; 32K * 4 * 2 = 256K 
    REP     STOSW               ; Clear dat memory! 
 
    MOV     AX, True            ; Return Success Code 
 
SVM_EXIT: 
 
    POP     DI                  ; Restore Saved Registers 
    POP     SI 
    POP     DS 
    POP     BP 
 
    RET     8                   ;We are Done.. Outa here 
 
SET_VGA_MODEX   ENDP 
 
 
;================== 
;SET_MODEX% (Mode%) 
;================== 
; 
; Quickie Mode Set - Sets Up Mode X to Default Configuration  
;  
; INPUT PARAMETERS:  
;  
; ModeType%  - The Desired Version of 256 Color Mode X  
;  
;   (0 to 7 - See Chart under SET_VGA_MODEX)  
;  
; RETURNS in AX:    0 (= Failure) or ffff (= Success)  
; 
 
SM_STACK    STRUC 
                DW  ?   ;BP 
                DD  ?   ;Caller 
    SM_Mode     DW  ? 
SM_STACK    ENDS 
 
    PUBLIC  SET_MODEX 
 
SET_MODEX   PROC    FAR 
 
    PUSH    BP                  ; Preserve Important 
    MOV     BP,SP               ; Set up Stack Frame 
 
    XOR     AX, AX              ; Assume Failure 
    MOV     BX, [BP].SM_Mode    ; Get Desired Mode # 
 
    CMP     BX, NUM_MODES       ; Is it 0..7? 
    JAE     @SX_Exit            ; If Not, don't Bother 
 
    PUSH    BX                  ; Push Mode Parameter 
 
    SHL     BX, 1                   ; Scale BX 
    MOV     SI, w CS:MODE_TABLE[BX] ;CS:SI -> Mode Info 
 
    MOV     AX, CS:[SI].M_XSize ; Get Default X Size 
    PUSH    AX                  ; Push X Size Parameter 
 
    MOV     AX, CS:[SI].M_Ysize ; Get Default Y size 
    PUSH    AX                  ; Push Y Size Parameter 
 
    MOV     AL, CS:[SI].M_Pages ; Get Default # of Pages 
    MOV     AH, 0               ; Hi Byte = 0 
    PUSH    AX                  ; Push # Pages 
 
    CALL    FAR PTR SET_VGA_MODEX   ; Set up Mode X! 
 
@SX_Exit: 
 
    POP     BP                  ; Restore Register 
    RET     2                   ; We are Done.. Outa here 
 
SET_MODEX   ENDP 
 
 
;============================ 
;CLEAR_VGA_SCREEN (ColorNum%) 
;============================ 
; 
; Clears the currently active display page to  
; all the color #ColorNum  
; 
 
CVS_STACK   STRUC 
                DW  ?,?     ;DI, BP 
                DD  ?       ;Caller 
    CVS_COLOR   DB  ?,?     ;Color to Set Screen to  
CVS_STACK ENDS 
 
    PUBLIC  CLEAR_VGA_SCREEN 
 
CLEAR_VGA_SCREEN    PROC    FAR 
 
    PUSH    BP                  ; Preserve Registers 
    PUSH    DI 
 
    MOV     BP, SP              ; Set up Stack Frame 
 
    OUT_16  SC_INDEX, ALL_PLANES_ON ; Select All Planes 
    LES     DI, d CURRENT_PAGE      ; Point to Active VGA Page 
 
    MOV     AL, [BP].CVS_COLOR  ; Get Color 
    MOV     AH, AL              ; Copy for Word Write 
    CLD                         ; Block fill Forwards 
 
    MOV     CX, PAGE_SIZE       ; Get Size of Page 
    SHR     CX, 1               ; Divied by 2 for Words 
    REP     STOSW               ; Block Fill VGA memory 
 
    POP     DI                  ; Restore Saved Registers 
    POP     BP 
 
    RET     2                   ;We are Done.. Outa here 
 
CLEAR_VGA_SCREEN    ENDP 
 
 
;=================================== 
;SET_POINT (Xpos%, Ypos%, ColorNum%) 
;=================================== 
; 
; Sets a single Pixel at (Xpos, Ypos) on the active  
; Display Page to color #ColorNum  
; 
 
SP_STACK    STRUC 
                DW  ?   ;BP 
                DD  ?   ;Caller 
    SETP_Color  DB  ?,? 
    SETP_Ypos   DW  ? 
    SETP_Xpos   DW  ? 
SP_STACK    ENDS 
 
        PUBLIC SET_POINT 
 
SET_POINT   PROC    FAR 
 
    PUSH    BP                  ; Preserve Registers 
    MOV     BP, SP              ; Set up Stack Frame 
 
    LES     DI, d CURRENT_PAGE  ; Point to Active VGA Page 
 
    MOV     AX, [BP].SETP_Ypos  ; Get Line # of Pixel 
    MUL     SCREEN_WIDTH        ; Get Offset to Start of Line 
 
    MOV     BX, [BP].SETP_Xpos  ; Get Xpos 
    MOV     CX, BX 
    SHR     BX, 2               ; X offset (Bytes) = Xpos/4 
    ADD     BX, AX              ; Offset = Width*Ypos + Xpos/4 
 
    MOV     AX, MAP_MASK_PLANE1 ; Map Mask & Plane Select Register 
    AND     CL, PLANE_BITS      ; Get Plane Bits 
    SHL     AH, CL              ; Get Plane Select Value 
    MOV     DX, SC_Index        ; Setup to select plane 
    OUT     DX, AX              ; Select Plane... 
 
    MOV     AL,[BP].SETP_Color  ; Get Pixel Color 
    MOV     ES:[DI+BX], AL      ; Draw Pixel 
 
    POP     BP                  ; Restore Saved Registers 
    RET     6                   ; Exit and Clean up Stack 
 
SET_POINT        ENDP 
 
 
;========================== 
;READ_POINT% (Xpos%, Ypos%) 
;========================== 
; 
; Returns the color of a pixel from the Active Display Page  
;  
; RETURNS:  AX = Color of Pixel at (Xpos, Ypos)  
; 
 
RP_STACK    STRUC 
            DW  ?   ;Bp 
            DD  ?   ;Caller 
    RP_Ypos DW  ? 
    RP_Xpos DW  ? 
RP_STACK    ENDS 
 
        PUBLIC  READ_POINT 
 
READ_POINT      PROC    FAR 
 
    PUSH    BP                  ; Preserve Registers 
    MOV     BP, SP              ; Set up Stack Frame 
 
    LES     DI, d CURRENT_PAGE  ; Point to Active VGA Page 
 
    MOV     AX, [Bp].RP_Ypos    ; Get Line # of Pixel 
    MUL     SCREEN_WIDTH        ; Get Offset to Start of Line 
 
    MOV     BX, [BP].RP_Xpos    ; Get Xpos 
    MOV     CX, BX 
    SHR     BX, 2               ; X offset (Bytes) = Xpos/4 
    ADD     BX, AX              ; Offset = Width*Ypos + Xpos/4 
 
    MOV     AL, READ_MAP        ; GC Read Mask Register 
    MOV     AH, CL              ; Get Xpos 
    AND     AH, PLANE_BITS      ; & mask out Plane # 
    MOV     DX, GC_INDEX        ; Setup to select Read Mask 
    OUT     DX, AX              ; Select Read Plane... 
 
    XOR     AX, AX              ; Clear Return Value 
 
    MOV     AL, ES:[DI+BX]      ; Get Color of Pixel 
 
    POP     BP                  ; Restore Saved Registers 
    RET     4                   ; Exit and Clean up Stack 
 
READ_POINT        ENDP 
 
 
;======================================================  
;FILL_BLOCK (Xpos1%, Ypos1%, Xpos2%, Ypos2%, ColorNum%)  
;======================================================  
;  
; Fills a block on the current display Page from Point  
; (Xpos1, Ypos1) to Point (Xpos2, Ypos2) in Color #ColorNum  
; 
 
FB_STACK    STRUC 
                DW  ?,?,?,? ; DS, DI, SI, BP 
                DD  ?       ; Caller 
    FB_Color    DB  ?,?     ; Fill Color 
    FB_Ypos2    DW  ?       ; Lower Right Pixel 
    FB_Xpos2    DW  ?       ; 
    FB_Ypos1    DW  ?       ; Upper Left Pixel 
    FB_Xpos1    DW  ?       ; 
FB_STACK    ENDS 
 
        PUBLIC    FILL_BLOCK 
 
FILL_BLOCK  PROC    FAR 
 
    PUSH    BP                  ; Save Registers 
    PUSH    SI 
    PUSH    DI 
    PUSH    DS 
 
    MOV     BP, SP              ; Set up Stack Frame 
 
    LES     DI, d CURRENT_PAGE  ; Point to Active VGA Page 
    CLD                         ; Direction Flag = Forward 
 
    OUT_8   SC_INDEX, MAP_MASK  ; Set up for Plane Select 
 
    ; Validate Pixel Coordinates 
    ; If necessary, Swap so X1 <= X2, Y1 <= Y2 
 
    MOV     AX, [BP].FB_Ypos1   ; AX = Y1   is Y1< Y2? 
    MOV     BX, [BP].FB_Ypos2   ; BX = Y2 
    CMP     AX, BX 
    JLE     @FB_NOSWAP1 
 
    MOV     [BP].FB_Ypos1, BX   ; Swap Y1 and Y2 and save Y1 
    XCHG    AX, BX              ; on stack for future use 
 
@FB_NOSWAP1: 
    SUB     BX, AX              ; Get Y width 
    INC     BX                  ; Add 1 to avoid 0 value 
    MOV     [BP].FB_Ypos2, BX   ; Save in Ypos2 
 
    MUL     SCREEN_WIDTH        ; Mul Y1 by Bytes per Line 
    ADD     DI, AX              ; DI = Start of Line Y1 
 
    MOV     AX, [BP].FB_Xpos1   ; Check X1 <= X2 
    MOV     BX, [BP].FB_Xpos2   ; 
    CMP     AX, BX 
    JLE     @FB_NOSWAP2         ; Skip Ahead if Ok 
 
    MOV     [BP].FB_Xpos2, AX   ; Swap X1 AND X2 and save X2 
    XCHG    AX, BX              ; on stack for future use 
 
    ; All our Input Values are in order, Now determine 
    ; How many full "bands" 4 pixels wide (aligned) there 
    ; are, and if there are partial bands (<4 pixels) on 
    ; the left and right edges. 
 
@FB_NOSWAP2: 
    MOV     DX, AX              ; DX = X1 (Pixel Position) 
    SHR     DX, 2               ; DX/4 = Bytes into Line 
    ADD     DI, DX              ; DI = Addr of Upper-Left Corner 
 
    MOV     CX, BX              ; CX = X2 (Pixel Position) 
    SHR     CX, 2               ; CX/4 = Bytes into Line 
 
    CMP     DX, CX              ; Start and end in same band? 
    JNE     @FB_NORMAL          ; if not, check for l & r edges 
    JMP     @FB_ONE_BAND_ONLY   ; if so, then special processing 
 
@FB_NORMAL: 
    SUB     CX, DX              ; CX = # bands -1 
    MOV     SI, AX              ; SI = PLANE#(X1) 
    AND     SI, PLANE_BITS          ; if Left edge is aligned then .. 
    JZ      @FB_LEFT_PLANE_FLUSH    ; no special processing.. 
 
    ; Draw "Left Edge" vertical strip of 1-3 pixels... 
 
    OUT_8   SC_Data, LEFT_CLIP_MASK[SI] ; Set Left Edge Plane Mask 
 
    MOV     SI, DI              ; SI = Copy of Start Addr (UL) 
 
    MOV     DX, [BP].FB_Ypos2   ; Get # of Lines to draw 
    MOV     AL, [BP].FB_Color   ; Get Fill Color 
    MOV     BX, SCREEN_WIDTH    ; Get Vertical increment Value 
 
@FB_LEFT_LOOP: 
    MOV     ES:[SI], AL         ; Fill in Left Edge Pixels 
    ADD     SI, BX              ; Point to Next Line (Below) 
    DEC     DX                  ; Lines to go-- 
    JZ      @FB_LEFT_CONT       ; Exit loop if all Lines Drawn 
 
    MOV     ES:[SI], AL         ; Fill in Left Edge Pixels 
    ADD     SI, BX              ; Point to Next Line (Below) 
    DEC     DX                  ; Lines to go-- 
    JNZ     @FB_LEFT_LOOP       ; loop until left strip is drawn 
 
@FB_LEFT_CONT: 
 
    INC     DI                  ; Point to Middle (or Right) Block 
    DEC     CX                  ; Reset CX insted of JMP @FB_RIGHT 
 
@FB_LEFT_PLANE_FLUSH: 
    INC     CX                  ; Add in Left band to middle block 
 
    ;DI = Addr of 1st middle Pixel (band) to fill 
    ;CX = # of Bands to fill -1 
 
@FB_RIGHT: 
    MOV     SI, [BP].FB_Xpos2       ; Get Xpos2 
    AND     SI, PLANE_BITS          ; Get Plane values 
    CMP     SI, 0003                ; Plane = 3? 
    JE      @FB_RIGHT_EDGE_FLUSH    ; Hey, add to middle 
 
    ; Draw "Right Edge" vertical strip of 1-3 pixels... 
 
    OUT_8   SC_Data, RIGHT_CLIP_MASK[SI]    ; Set Right Edge Plane Mask 
 
    MOV     SI, DI              ; Get Addr of Left Edge 
    ADD     SI, CX              ; Add Width-1 (Bands) 
    DEC     SI                  ; To point to top of Right Edge 
 
    MOV     DX, [BP].FB_Ypos2   ; Get # of Lines to draw 
    MOV     AL, [BP].FB_Color   ; Get Fill Color 
    MOV     BX, SCREEN_WIDTH    ; Get Vertical increment Value 
 
@FB_RIGHT_LOOP: 
    MOV     ES:[SI], AL         ; Fill in Right Edge Pixels 
    ADD     SI, BX              ; Point to Next Line (Below) 
    DEC     DX                  ; Lines to go-- 
    JZ      @FB_RIGHT_CONT      ; Exit loop if all Lines Drawn 
 
    MOV     ES:[SI], AL         ; Fill in Right Edge Pixels 
    ADD     SI, BX              ; Point to Next Line (Below) 
    DEC     DX                  ; Lines to go-- 
    JNZ     @FB_RIGHT_LOOP      ; loop until left strip is drawn 
 
@FB_RIGHT_CONT: 
 
    DEC     CX                  ; Minus 1 for Middle bands 
    JCXZ    @FB_EXIT            ; Uh.. no Middle bands... 
 
@FB_RIGHT_EDGE_FLUSH: 
 
    ;DI = Addr of Upper Left block to fill 
    ;CX = # of Bands to fill in (width) 
 
    OUT_8   SC_Data, 0FH        ; Write to All Planes 
 
    MOV     DX, SCREEN_WIDTH    ; DX = DI Incrament 
    SUB     DX, CX              ;  = Screen_Width-# Planes Filled 
 
    MOV     BX, CX              ; BX = Quick Refill for CX 
    MOV     SI, [BP].FB_Ypos2   ; SI = # of Line to Fill 
    MOV     AL, [BP].FB_Color   ; Get Fill Color 
 
@FB_MIDDLE_LOOP: 
    REP     STOSB               ; Fill in entire line 
 
    MOV     CX, BX              ; Recharge CX (Line Width) 
    ADD     DI, DX              ; Point to start of Next Line 
 
    DEC     SI                  ; Lines to go-- 
    JNZ     @FB_MIDDLE_LOOP     ; Loop until all lines drawn 
 
    JMP     @FB_EXIT            ; Outa here 
 
@FB_ONE_BAND_ONLY: 
    MOV     SI, AX                  ; Get Left CLip Mask, Save X1 
    AND     SI, PLANE_BITS          ; Mask out Row # 
    MOV     AL, LEFT_CLIP_MASK[SI]  ; Get Left Edge Mask 
    MOV     SI, BX                  ; Get Right Clip Mask, Save X2 
    AND     SI, PLANE_BITS          ; Mask out Row # 
    AND     AL, RIGHT_CLIP_MASK[SI] ; Get Right Edge Mask byte 
 
    MOV     DX, SC_Data         ; Plane Selection 
    OUT     DX, AL              ; Clip For Left & Right Masks 
 
    MOV     CX, [BP].FB_Ypos2   ; Get # of Lines to draw 
    MOV     AL, [BP].FB_Color   ; Get Fill Color 
    MOV     BX, SCREEN_WIDTH    ; Get Vertical increment Value 
 
@FB_ONE_LOOP: 
    MOV     ES:[DI], AL         ; Fill in Pixels 
    ADD     DI, BX              ; Point to Next Line (Below) 
    DEC     CX                  ; Lines to go-- 
    JZ      @FB_EXIT            ; Exit loop if all Lines Drawn 
 
    MOV     ES:[DI], AL         ; Fill in Pixels 
    ADD     DI, BX              ; Point to Next Line (Below) 
    DEC     CX                  ; Lines to go-- 
    JNZ     @FB_ONE_LOOP        ; loop until left strip is drawn 
 
@FB_EXIT: 
    POP     DS 
    POP     DI 
    POP     SI 
    POP     BP                  ; Restore Registers 
 
    RET     10                  ; Exit and Clean up Stack 
 
FILL_BLOCK   ENDP 
 
 
;=====================================================  
;DRAW_LINE (Xpos1%, Ypos1%, Xpos2%, Ypos2%, ColorNum%)  
;=====================================================  
;  
; Draws a Line from (X1,Y1) to (X2,Y2) in Color #ColorNum  
; in VGA Mode X  
; 
 
DL_STACK    STRUC 
                DW  ?,?,?   ;DI, SI , BP 
                DD  ?       ;Caller 
    DL_ColorF   DB  ?,? 
    DL_Ypos2    DW  ? 
    DL_Xpos2    DW  ? 
    DL_Ypos1    DW  ? 
    DL_Xpos1    DW  ? 
DL_STACK    ENDS 
 
        PUBLIC DRAW_LINE 
 
DRAW_LINE   PROC    FAR 
 
    PUSH    BP                  ; Save Registers 
    PUSH    SI 
    PUSH    DI 
 
    MOV     BP, SP              ; Set up Stack Frame 
    CLD                         ; Direction Flag = Forward 
 
    OUT_8   SC_INDEX, MAP_MASK  ; Set up for Plane Select 
 
    MOV     CH, [BP].DL_ColorF  ; Save Color in CH 
 
    ;Check Line Type 
 
    MOV     SI, [BP].DL_Xpos1   ; AX = X1   is X1< X2? 
    MOV     DI, [BP].DL_Xpos2   ; DX = X2 
    CMP     SI, DI              ; Is X1 < X2 
    JE      @DL_VLINE           ; If X1=X2, Draw Vertical Line 
    JL      @DL_NOSWAP1         ; If X1 < X2, don't swap 
 
    XCHG    SI, DI              ; X2 IS > X1, SO SWAP THEM 
 
@DL_NOSWAP1: 
 
    ; SI = X1, DI = X2 
 
    MOV     AX, [BP].DL_Ypos1   ; AX = Y1   is Y1 <> Y2? 
    CMP     AX, [BP].DL_Ypos2   ; Y1 = Y2? 
    JE      @DL_HORZ            ; If so, Draw a Horizontal Line 
 
    JMP     @DL_BREZHAM         ; Diagonal line... go do it... 
 
    ; This Code draws a Horizontal Line in Mode X where: 
    ; SI = X1, DI = X2, and AX = Y1/Y2 
 
@DL_HORZ: 
 
    MUL     SCREEN_WIDTH        ; Offset = Ypos * Screen_Width 
    MOV     DX, AX              ; CX = Line offste into Page 
 
    MOV     AX, SI                  ; Get Left Edge CLip Mask, Save X1 
    AND     SI, PLANE_BITS          ; Mask out Row # 
    MOV     BL, LEFT_CLIP_MASK[SI]  ; Get Left Edge Mask 
    MOV     CX, DI                  ; Get Right Edge Clip Mask, Save X2 
    AND     DI, PLANE_BITS          ; Mask out Row # 
    MOV     BH, RIGHT_CLIP_MASK[DI] ; Get Right Edge Mask byte 
 
    SHR     AX, 2               ; Get X1 Byte # (=X1/4) 
    SHR     CX, 2               ; Get X2 Byte # (=X2/4) 
 
    LES     DI, d CURRENT_PAGE  ; Point to Active VGA Page 
    ADD     DI, DX              ; Point to Start of Line 
    ADD     DI, AX              ; Point to Pixel X1 
 
    SUB     CX, AX              ; CX = # Of Bands (-1) to set 
    JNZ     @DL_LONGLN          ; If longer than one segment, go ahead 
 
    AND     BL, BH              ; otherwise, megre clip masks 
 
@DL_LONGLN: 
 
    OUT_8   SC_Data, BL         ; Set the Left Clip Mask 
 
    MOV     AL, [BP].DL_ColorF  ; Get Line Color 
    MOV     BL, AL              ; BL = Copy of Line Color 
    STOSB                       ; Set Left (1-4) Pixels 
 
    JCXZ    @DL_EXIT            ; Done if only one Line Segment 
 
    DEC     CX                  ; CX = # of Middle Segemnts 
    JZ      @DL_XRSEG           ; If no middle segments.... 
 
    ;Draw Middle Segments 
 
    MOV     AL, ALL_PLANES      ; Write to ALL Planes 
    OUT     DX, AL              ; Select Planes 
 
    MOV     AL, BL              ; Get Color from BL 
    REP     STOSB               ; Draw Middle (4 Pixel) Segements 
 
@DL_XRSEG: 
    MOV     AL, BH              ; Get Right Clip Mask 
    OUT     DX, AL              ; Select Planes 
 
    MOV     AL, BL              ; Get Color Value 
    STOSB                       ; Draw Right (1-4) Pixels 
 
    JMP SHORT @DL_EXIT          ; We Are Done... 
 
    ; This Code Draws A Vertical Line.  On entry: 
    ; CH = Line Color, SI & DI = X1 
 
@DL_VLINE: 
 
    MOV     AX, [BP].DL_Ypos1   ; AX = Y1 
    MOV     SI, [BP].DL_Ypos2   ; SI = Y2 
    CMP     AX, SI              ; Is Y1 < Y2? 
    JLE     @DL_NOSWAP2         ; if so, Don't Swap them 
 
    XCHG    AX, SI              ; Ok, NOW Y1 < Y2 
 
@DL_NOSWAP2: 
 
    SUB     SI, AX              ; SI = Line Heigth (Y2-Y1+1) 
    INC     SI 
 
    ; AX = Y1, DI = X1, Get offset into Page into AX 
 
    MUL     SCREEN_WIDTH        ; Offset = Y1 (AX) * Screen Width 
    MOV     DX, DI              ; Copy Xpos into DX 
    SHR     DI, 2               ; DI = Xpos/4 
    ADD     AX, DI              ; DI = Xpos/4 + ScreenWidth * Y1 
 
    LES     DI, d CURRENT_PAGE  ; Point to Active VGA Page 
    ADD     DI, AX              ; Point to Pixel X1, Y1 
 
    ;Select Plane 
 
    MOV     CL, DL              ; CL = Save X1 
    AND     CL, PLANE_BITS      ; Get X1 MOD 4 (Plane #) 
    MOV     AX, MAP_MASK_PLANE1 ; Code to set Plane #1 
    SHL     AH, CL              ; Change to Correct Plane # 
 
    MOV     DX, SC_Index        ; Set VGA Sequencer 
    OUT     DX, AX              ; Select Plane! 
 
    MOV     AL, CH              ; Get Saved Color 
    MOV     BX, SCREEN_WIDTH    ; Get Offset to Advance Line By 
 
@DL_VLoop: 
    MOV     ES:[DI], AL         ; Draw Single Pixel 
    ADD     DI, BX              ; Point to Next Line 
 
    DEC     SI                  ; Lines to draw-- 
    JNZ     @DL_VLoop           ; Loop until Done 
 
@DL_EXIT: 
 
    JMP @DL_EXIT2               ; Done! 
 
    ; This code Draws a diagonal line in Mode X 
 
@DL_BREZHAM: 
    LES     DI, d CURRENT_PAGE  ; Point to Active VGA Page 
 
    MOV     AX, [BP].DL_Ypos1   ; get y1 value 
    MOV     BX, [BP].DL_Ypos2   ; get y2 value 
    MOV     CX, [BP].DL_Xpos1   ; Get Starting Xpos 
 
    CMP     BX, AX              ; Y2-Y1 is? 
    JNC     @DL_DeltaYOK        ; if Y2>=Y1 then goto... 
 
    XCHG    BX, AX              ; Swap em... 
    MOV     CX, [BP].DL_Xpos2   ; Get New Starting Xpos 
 
@DL_DeltaYOK: 
    MUL     SCREEN_WIDTH        ; Offset = SCREEN_WIDTH * Y1 
 
    ADD     DI, AX              ; DI -> Start of Line Y1 on Page 
    MOV     AX, CX              ; AX = Xpos (X1) 
    SHR     AX, 2               ; /4 = Byte Offset into Line 
    ADD     DI, AX              ; DI = Starting pos (X1,Y1) 
 
    MOV     AL, 11h             ; Staring Mask 
    AND     CL, PLANE_BITS      ; Get Plane # 
    SHL     AL, CL              ; and shift into placE 
    MOV     AH, [BP].DL_ColorF  ; Color in Hi Bytes 
 
    PUSH    AX                  ; Save Mask,Color... 
 
    MOV     DX, SC_Index 
    MOV     AH, AL              ; Plane # in AH 
    MOV     AL, MAP_MASK        ; Select Plane Register 
    OUT     DX, AX              ; Select initial plane 
 
    MOV     AX, [BP].DL_Xpos1   ; get x1 value 
    MOV     BX, [BP].DL_Ypos1   ; get y1 value 
    MOV     CX, [BP].DL_Xpos2   ; get x2 value 
    MOV     DX, [BP].DL_Ypos2   ; get y2 value 
 
    MOV     BP, SCREEN_WIDTH    ; Use BP for Line width to 
                                ; to avoid extra memory access 
 
    SUB     DX, BX              ; figure delta_y 
    JNC     @DL_DeltaYOK2       ; jump if y2>=y1 
 
    ADD     BX, DX              ; put y2 into y1 
    NEG     DX                  ; abs(Delta_y) 
    XCHG    AX, CX              ; and exchange x1 and x2 
 
@DL_DeltaYOK2: 
    MOV     BX, 08000H          ; seed for fraction accumulator 
 
    SUB     CX, AX              ; figure delta_x 
    JC      @DL_DrawLeft        ; if negitive, go left 
 
    JMP     @DL_DrawRight       ; Draw Line that slopes right 
 
@DL_DrawLeft: 
 
    NEG     CX                  ; abs(Delta_X) 
 
    CMP     CX, DX              ; is X < Delta_Y? 
    JB      @DL_SteepLeft       ; yes, so go do steep line (Delta_Y iterations) 
 
    ; Draw a Shallow line to the left in Mode X 
 
@DL_ShallowLeft: 
    XOR     AX, AX              ; zero low word of Delta_y * 10000h 
    SUB     AX, DX              ; DX:AX <- DX * 0FFFFh 
    SBB     DX, 0 
    DIV     CX                  ; divide by Delta_X 
 
    MOV     SI, BX              ; Si = Acculmulator 
    MOV     BX, AX              ; Bx = Addor 
    POP     AX                  ; Get Color, Bit mask 
    MOV     DX, SC_Data         ; Sequence controller data register 
    INC     CX                  ; Inc Delta_X so we can unroll loop 
 
    ; Loop (x2) to Draw Pixels, Move Left, and Maybe Down... 
 
@DL_SLLLoop: 
    MOV     ES:[DI], AH         ; set first pixel, plane data set up 
 
    DEC     CX                  ; count down Delta_X 
    JZ      @DL_SLLExit         ; Exit if done.. 
 
    ADD     SI, BX              ; add numerator to accumulator 
    JNC     @DL_SLLL2nc         ; move down on carry 
 
    ADD     DI, BP              ; Move Down one line... 
 
@DL_SLLL2nc: 
    DEC     DI                  ; Left one addr 
    ROR     AL, 1               ; Move Left one plane 
    CMP     AL, 87h             ; Skipped around?, if AL <88 then Carry set 
    ADC     DI, 0               ; Adjust Address: DI = Di + Carry, ie. plane = 
    OUT     DX, AL              ; Set up New Bit Plane mask 
 
    MOV     ES:[DI], AH         ; set pixel 
 
    ADD     SI, BX              ; add numerator to accumulator, 
    JNC     @DL_SLLL3nc         ; move down on carry 
 
    ADD     DI, BP              ; Move Down one line... 
 
@DL_SLLL3nc:                    ; Now move left a pixel... 
    DEC     DI                  ; Left one addr 
    ROR     AL, 1               ; Move Left one plane 
    CMP     AL, 87h             ; Skipped around?, if AL <88 then Carry set 
    ADC     DI, 0               ; Adjust Address: DI = Di + Carry, ie. plane = 
    OUT     DX, AL              ; Set up New Bit Plane mask 
 
    DEC     CX                  ; count down Delta_X 
    JNZ     @DL_SLLLoop         ; loop till done 
 
@DL_SLLExit: 
    JMP     @DL_EXIT2           ; and exit 
 
    ; Draw a steep line to the left in Mode X 
 
@DL_SteepLeft: 
    XOR     AX, AX              ; zero low word of Delta_y * 10000h 
    XCHG    DX, CX              ; Delta_Y switched with Delta_x 
    DIV     CX                  ; divide by Delta_Y 
 
    MOV     SI, BX              ; Si = Acculmulator 
    MOV     BX, AX              ; Bx = Addor 
    POP     AX                  ; Get Color, Bit mask 
    MOV     DX, SC_Data         ; Sequence controller data register 
    INC     CX                  ; Inc Delta_Y so we can unroll loop 
 
    ; Loop (x2) to Draw Pixels, Move Down, and Maybe left 
 
@DL_STLLoop: 
 
    MOV     ES:[DI], AH         ; set first pixel 
    DEC     CX                  ; Count Down Delta_Y 
    JZ      @DL_STLExit         ; Exit if done 
 
    ADD     SI, BX              ; add numerator to accumulator 
    JNC     @DL_STLnc2          ; No carry, just move down! 
 
    ; Move Left 
 
    DEC     DI                  ; Left one addr 
    ROR     AL, 1               ; Move Left one plane 
    CMP     AL, 87h             ; Skipped around?, if AL <88 then Carry set 
    ADC     DI, 0               ; Adjust Address: DI = Di + Carry, ie. plane = 
    OUT     DX, AL              ; Set up New Bit Plane mask 
 
@DL_STLnc2: 
    ADD     DI, BP              ; advance to next line. 
 
    MOV     ES:[DI], AH         ; set pixel 
    ADD     SI, BX              ; add numerator to accumulator 
    JNC     @DL_STLnc3          ; No carry, just move down! 
 
    ;Move Left 
 
    DEC     DI                  ; Left one addr 
    ROR     AL, 1               ; Move Left one plane 
    CMP     AL, 87h             ; Skipped around?, if AL <88 then Carry set 
    ADC     DI, 0               ; Adjust Address: DI = Di + Carry, ie. plane = 
    OUT     DX, AL              ; Set up New Bit Plane mask 
 
@DL_STLnc3: 
    ADD     DI, BP              ; advance to next line. 
 
    DEC     CX                  ; count down Delta_Y 
    JNZ     @DL_STLLoop         ; loop till done 
 
@DL_STLExit: 
    JMP     @DL_EXIT2           ; and exit 
 
    ; Draw A line that goes to the Right... 
 
@DL_DrawRight: 
    CMP     CX, DX              ; is X < Delta_Y? 
    JB      @DL_SteepRight      ; yes, so go do steep line (Delta_Y iterations) 
 
    ; Draw a Shallow line to the Right in Mode X 
 
@DL_ShallowRight: 
    XOR     AX, AX              ; zero low word of Delta_y * 10000h 
    SUB     AX, DX              ; DX:AX <- DX * 0FFFFh 
    SBB     DX, 0 
    DIV     CX                  ; divide by Delta_X 
 
    MOV     SI, BX              ; Si = Acculmulator 
    MOV     BX, AX              ; Bx = Addor 
    POP     AX                  ; Get Color, Bit mask 
    MOV     DX, SC_Data         ; Sequence controller data register 
    INC     CX                  ; Inc Delta_X so we can unroll loop 
 
    ; Loop (x2) to Draw Pixels, Move Right, and Maybe Down... 
 
@DL_SLRLoop: 
    MOV     ES:[DI], AH         ; set first pixel, mask already set up 
 
    DEC     CX                  ; count down Delta_X 
    JZ      @DL_SLRExit         ; Exit if done.. 
 
    ADD     SI, BX              ; add numerator to accumulator 
    JNC     @DL_SLR2nc          ; don't move down if carry not set.... 
 
    ADD     DI, BP              ; Move Down one line... 
 
@DL_SLR2nc:                     ; Now move right a pixel... 
    ROL     AL, 1               ; Move Right one plane 
    CMP     AL, 12h             ; Skipped around?, if AL >12 then Carry not set 
    ADC     DI, 0               ; Adjust Address: DI = Di + Carry, ie. plane = 
    OUT     DX, AL              ; Set up New Bit Plane mask 
 
    MOV     ES:[DI], AH         ; set pixel 
    ADD     SI, BX              ; add numerator to accumulator 
    JNC     @DL_SLR3nc          ; don't move down if carry not set.... 
 
    ADD     DI, BP              ; Move Down one line... 
 
@DL_SLR3nc: 
    ROL     AL, 1               ; Move Right one plane 
    CMP     AL, 12h             ; Skipped around?, if AL >12 then Carry not set 
    ADC     DI, 0               ; Adjust Address: DI = Di + Carry, ie. plane = 
    OUT     DX, AL              ; Set up New Bit Plane mask 
 
    DEC     CX                  ; count down Delta_X 
    JNZ     @DL_SLRLoop         ; loop till done 
 
@DL_SLRExit: 
    JMP     @DL_EXIT2           ; and exit 
 
    ; Draw a Shallow line to the Right in Mode X 
 
@DL_SteepRight: 
    XOR     AX, AX              ; zero low word of Delta_y * 10000h 
    XCHG    DX, CX              ; Delta_Y switched with Delta_x 
    DIV     CX                  ; divide by Delta_Y 
 
    MOV     SI, BX              ; Si = Acculmulator 
    MOV     BX, AX              ; Bx = Addor 
    POP     AX                  ; Get Color, Bit mask 
    MOV     DX, SC_Data         ; Sequence controller data register 
    INC     CX                  ; Inc Delta_Y so we can unroll loop 
 
    ; Loop (x2) to Draw Pixels, Move Down, and Maybe Right 
 
@STRLoop: 
    MOV     ES:[DI], AH         ; set first pixel, mask already set up... 
 
    DEC     CX                  ; count down Delta_Y 
    JZ      @DL_EXIT2           ; Exit if Finished 
 
    ADD     SI, BX              ; add numerator to accumulator 
    JNC     @STRnc2             ; if no carry then just go down... 
 
    ROL     AL, 1               ; Move Right one plane 
    CMP     AL, 12h             ; Skipped around?, if AL >12 then Carry not set 
    ADC     DI, 0               ; Adjust Address: DI = Di + Carry, ie. plane = 
    OUT     DX, AL              ; Set up New Bit Plane mask 
 
@STRnc2: 
    ADD     DI, BP              ; advance to next line. 
 
    MOV     ES:[DI], AH         ; set pixel 
    ADD     SI, BX              ; add numerator to accumulator 
    JNC     @STRnc3             ; if no carry then just go down... 
 
    ROL     AL, 1               ; Move Right one plane 
    CMP     AL, 12h             ; Skipped around?, if AL >12 then Carry not set 
    ADC     DI, 0               ; Adjust Address: DI = Di + Carry, ie. plane = 
    OUT     DX, AL              ; Set up New Bit Plane mask 
 
@STRnc3: 
    ADD     DI, BP              ; advance to next line. 
 
    DEC     CX                  ; count down Delta_Y 
    JNZ     @STRLoop            ; loop till done 
 
@DL_EXIT2: 
 
    POP     DI 
    POP     SI 
    POP     BP                  ; Restore BP, SI, & DI 
 
    RET     10                  ; Exit and Clean up Stack 
 
DRAW_LINE        ENDP 
 
 
;========================= 
;SET_ACTIVE_PAGE (PageNo%) 
;========================= 
; 
;Sets the Video Page to be used for future drawing  
; 
 
SAP_STACK   STRUC 
                DW  ?       ;BP 
                DD  ?       ;Caller 
    SAP_Page    DW  ?       ;Page # for Drawing  
SAP_STACK   ENDS 
 
    PUBLIC  SET_ACTIVE_PAGE 
 
SET_ACTIVE_PAGE PROC    FAR 
 
    PUSH    BP                  ; Preserve Registers 
    MOV     BP, SP              ; Set up Stack Frame 
 
    MOV     BX, [BP].SAP_Page   ; Get Desired Page # 
    CMP     BX, LAST_PAGE       ; Is Page # Valid? 
    JAE     @SAP_Exit           ; IF Not, Do Nothing 
 
    MOV     ACTIVE_PAGE, BX     ; Set Active Page # 
 
    SHL     BX, 1               ; Scale Page # to Word 
    MOV     AX, PAGE_ADDR[BX]   ; Get offset to Page 
 
    MOV     CURRENT_PAGE, AX    ; And set for future LES's 
 
@SAP_Exit: 
    POP     BP                  ; Restore Registers 
 
    RET     2                   ; We are Done.. Outa here 
 
SET_ACTIVE_PAGE ENDP 
 
 
;================ 
;GET_ACTIVE_PAGE% 
;================ 
; 
; Returns the Video Page # currently used for Drawing  
;  
; RETURNS:   AX = Current Video Page used for Drawing  
; 
 
    PUBLIC  GET_ACTIVE_PAGE 
 
GET_ACTIVE_PAGE PROC    FAR 
 
    MOV     AX, ACTIVE_PAGE     ; Get Active Page # 
 
    RET                         ; We are Done.. Outa here 
 
GET_ACTIVE_PAGE ENDP 
 
 
;========================== 
;SET_DISPLAY_PAGE (PageNo%) 
;========================== 
; 
;Sets the Video Page to be displayed on the screen  
; 
 
 
SDP_STACK   STRUC 
                DW  ?       ;BP 
                DD  ?       ;Caller 
    SDP_Page    DW  ?       ;Page # to Display...  
SDP_STACK   ENDS 
 
    PUBLIC  SET_DISPLAY_PAGE 
 
SET_DISPLAY_PAGE    PROC    FAR 
 
    PUSH    BP                  ; Preserve Registers 
    MOV     BP, SP              ; Set up Stack Frame 
 
    MOV     BX, [BP].SDP_Page   ; Get Desired Page # 
    CMP     BX, LAST_PAGE       ; Is Page # Valid? 
    JAE     @SDP_Exit           ; IF Not, Do Nothing 
 
    MOV     DISPLAY_PAGE, BX    ; Set Display Page # 
 
    SHL     BX, 1               ; Scale Page # to Word 
    MOV     CX, PAGE_ADDR[BX]   ; Get offset in memory to Page 
 
    ADD     CX, CURRENT_MOFFSET ; Adjust for any scrolling 
 
    MOV     DX, CRTC_Index      ; We Change the VGA Sequencer 
    MOV     AL, START_DISP_LO   ; Display Start Low Register 
    MOV     AH, CL              ; Low 8 Bits of Start Addr 
    OUT     DX, AX              ; Set Display Addr Low 
 
    MOV     AL, START_DISP_HI   ; Display Start High Register 
    MOV     AH, CH              ; High 8 Bits of Start Addr 
    OUT     DX, AX              ; Set Display Addr High 
 
@SDP_Exit: 
    POP     BP                  ; Restore Registers 
 
    RET     2                   ; We are Done.. Outa here 
 
SET_DISPLAY_PAGE    ENDP 
 
 
;================= 
;GET_DISPLAY_PAGE% 
;================= 
; 
; Returns the Video Page # currently be displayed on the screen  
;  
; RETURNS: AX = Current Display Page  
; 
 
    PUBLIC  GET_DISPLAY_PAGE 
 
GET_DISPLAY_PAGE    PROC    FAR 
 
    MOV     AX, DISPLAY_PAGE    ; Get Display Page # 
 
    RET                         ; We are Done.. Outa here 
 
GET_DISPLAY_PAGE    ENDP 
 
 
;=================================================  
;SET_DAC_REGISTER (Register%, Red%, Green%, Blue%)  
;=================================================  
;  
; Sets a single (RGB) Vga Palette Register  
; to the specified Reg, Green & Blue Values  
; 
 
SDR_STACK   STRUC 
                    DW  ?   ; BP 
                    DD  ?   ; Caller 
    SDR_Blue        DB  ?,? ; Blue Data Value 
    SDR_Green       DB  ?,? ; Green Data Value 
    SDR_Red         DB  ?,? ; Red Data Value 
    SDR_Register    DB  ?,? ; Palette Register #  
SDR_STACK   ENDS 
 
    PUBLIC  SET_DAC_REGISTER 
 
SET_DAC_REGISTER    PROC    FAR 
 
    PUSH    BP                  ; Save Bp 
    MOV     BP, SP              ; Set up Stack Frame 
 
    ; Select which DAC Register to modify 
 
    OUT_8   DAC_WRITE_ADDR, [BP].SDR_Register 
 
    MOV     DX, PEL_DATA_REG    ; Dac Data Register 
 
    MOV     AL, [Bp].SDR_Red    ; Get Red Intensity 
    OUT     DX, AL              ; Set it 
 
    MOV     AL, [Bp].SDR_Green  ; Get Green Intensity 
    OUT     DX, AL              ; Set it 
 
    MOV     AL, [Bp].SDR_Blue   ; Get Blue Intensity 
    OUT     DX, AL              ; Set it 
 
    POP     BP                  ; Restore Registers 
    RET     8                   ; Exit & Clean Up Stack 
 
SET_DAC_REGISTER    ENDP 
 
 
;====================================================  
;GET_DAC_REGISTER (Register%, &Red%, &Green%, &Blue%)  
;====================================================  
;  
; Gets the RGB Values of a single Vga Palette Register  
;  
; INPUT:  Red%, Green% & Blue% are offsets into DGROUP  
;         that point to 16 bit integers that will hold  
;         the returned red, green, and blue values  
; 
 
GDR_STACK   STRUC 
                    DW  ?   ; BP 
                    DD  ?   ; Caller 
    GDR_Blue        DW  ?   ; Addr of Blue Data Value in DS 
    GDR_Green       DW  ?   ; Addr of Green Data Value in DS 
    GDR_Red         DW  ?   ; Addr of Red Data Value in DS 
    GDR_Register    DB  ?,? ; Palette Register #  
GDR_STACK   ENDS 
 
    PUBLIC  GET_DAC_REGISTER 
 
GET_DAC_REGISTER    PROC    FAR 
 
    PUSH    BP                  ; Save Bp 
    MOV     BP, SP              ; Set up Stack Frame 
 
    ; Select which DAC Register to read in 
 
    OUT_8   DAC_READ_ADDR, [BP].GDR_Register 
 
    MOV     DX, PEL_DATA_REG    ; Dac Data Register 
    XOR     AX, AX              ; Clear AX 
 
    IN      AL, DX              ; Read Red Value 
    MOV     BX, [Bp].GDR_Red    ; Get Address of Red% 
    MOV     [BX], AX            ; *Red% = AX 
 
    IN      AL, DX              ; Read Green Value 
    MOV     BX, [Bp].GDR_Green  ; Get Address of Green% 
    MOV     [BX], AX            ; *Green% = AX 
 
    IN      AL, DX              ; Read Blue Value 
    MOV     BX, [Bp].GDR_Blue   ; Get Address of Blue% 
    MOV     [BX], AX            ; *Blue% = AX 
 
    POP     BP                  ; Restore Registers 
    RET     8                   ; Exit & Clean Up Stack 
 
GET_DAC_REGISTER    ENDP 
 
 
;========================= 
;SET_WINDOW (Xpos%, Ypos%) 
;========================= 
; 
; Since a Logical Screen can be larger than the Physical  
; Screen, Scrolling is possible.  This routine sets the  
; Upper Left Corner of the Screen to the specified Pixel.  
;  
; Xpos & Ypos  = Coordinates of the Pixel to put in the  
;                upper left corner of the screen  
; 
 
SW_STACK    STRUC 
                DW  ?   ;BP 
                DD  ?   ;Caller 
    SW_Ypos     DW  ? 
    SW_Xpos     DW  ? 
SW_STACK    ENDS 
 
        PUBLIC SET_WINDOW 
 
SET_WINDOW  PROC    FAR 
 
    PUSH    BP                  ; Preserve Registers 
    MOV     BP, SP              ; Set up Stack Frame 
 
    ; Check if our Scroll Offsets are Valid 
 
    MOV     AX, [BP].SW_Ypos    ; Get Desired Y Offset 
    CMP     AX, MAX_YOFFSET     ; Is it Within Limitss? 
    JA      @SW_Exit            ; if not, exit 
 
    MOV     BX, [BP].SW_Xpos    ; Get Desired X Offset 
    CMP     BX, MAX_XOFFSET     ; Is it Within Limitss? 
    JA      @SW_Exit            ; if not, exit 
 
    ; Compute proper Display start address to use 
 
    MUL     SCREEN_WIDTH        ; AX = YOffset * Line Width 
    MOV     CX, BX              ; CX = Copy of X Offset 
    SHR     BX, 2               ; BX / 4 = Bytes into Line 
    ADD     AX, BX              ; AX = Offset of Upper Left Pixel 
 
    MOV     CURRENT_MOFFSET, AX ; Save Offset Info 
 
    MOV     BX, DISPLAY_PAGE    ; Get Display Page # 
    SHL     BX, 1               ; Scale Page # to Word 
    ADD     AX, PAGE_ADDR[BX]   ; Get offset in VGA to Page 
    MOV     BX, AX              ; BX = Desired Display Start 
 
    MOV     DX, INPUT_1         ; Input Status #1 Register 
 
    ; Wait for a Vertical Retrace to smooth out the scroll 
 
@SW_WAIT: 
    IN      AL, DX              ; Get VGA stauts 
    JMP     $+2                 ; Delay (why?) 
    AND     AL, 8               ; Bit 3 Gone Hi Yet? 
    JZ      @SW_WAIT            ; If Not, wait for it 
    CLI                         ; Don't Interrupt this! 
 
    ; Set the Start Display Address to the New window 
 
    MOV     DX, CRTC_Index      ; We Change the VGA Sequencer 
    MOV     AL, START_DISP_LO   ; Display Start Low Register 
    MOV     AH, BL              ; Low 8 Bits of Start Addr 
    OUT     DX, AX              ; Set Display Addr Low 
 
    MOV     AL, START_DISP_HI   ; Display Start High Register 
    MOV     AH, BH              ; High 8 Bits of Start Addr 
    OUT     DX, AX              ; Set Display Addr High 
 
    ; Now Set the Horizontal Pixel Pan values 
 
    MOV     DX, INPUT_1         ; Input Status #1 Register 
    IN      AL, DX              ; Reset Attrib Flip/Flop 
 
    OUT_8   ATTRIB_Ctrl, 33h    ; Select Pixel Pan Register 
 
    MOV     AL, CL              ; Get Low Bits of X Offset 
    AND     AL, 03              ; Get # of Pixels to Pan (0-3) 
    SHL     AL, 1               ; Shift for 256 Color Mode 
    OUT     DX, AX              ; Fine tune the display! 
    STI                         ; Ok, Now you CAN interrupt 
 
@SW_Exit: 
    POP     BP                  ; Restore Saved Registers 
    RET     4                   ; Exit and Clean up Stack 
 
SET_WINDOW        ENDP 
 
    END