;======================================================== ; MODEX.ASM - A Complete Mode X Library ; ; Version 1.04 Release, 3 May 1993, 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 routines by Matt Pritchard" ; in the credits. ; ; ========================================================= ; ; All of this code is designed to be assembled with MASM 5.10a ; but TASM 3.0 could be used as well. ; ; The routines contained are designed for use in a MEDIUM model ; program. All Routines are FAR, and is assumed that a DGROUP ; data segment exists and that DS will point to it on entry. ; ; For all routines, the AX, BX, CX, DX, ES and FLAGS registers ; will not be preserved, while the DS, BP, SI and DI registers ; will be preserved. ; ; Unless specifically noted, All Parameters are assumed to be ; "PASSED BY VALUE". That is, the actual value is placed on ; the stack. When a reference is passed it is assumed to be ; a near pointer to a variable in the DGROUP segment. ; ; Routines that return a single 16-Bit integer value will ; return that value in the AX register. ; ; This code will *NOT* run on an 8086/8088 because 80286+ ; specific instructions are used. If you have an 8088/86 ; and VGA, you can buy an 80386-40 motherboard for about ; $160 and move into the 90's. ; ; This code is reasonably optimized: Most drawing loops have ; been unrolled once and memory references are minimized by ; keeping stuff in registers when possible. ; ; Error Trapping varies by Routine. No Clipping is performed ; so the caller should verify that all coordinates are valid. ; ; Several Macros are used to simplify common 2 or 3 instruction ; sequences. Several Single letter Text Constants also ; simplify common assembler expressions like "WORD PTR". ; ; ------------------ Mode X Variations ------------------ ; ; Mode # Screen Size Max Pages Aspect Ratio (X:Y) ; ; 0 320 x 200 4 Pages 1.2:1 ; 1 320 x 400 2 Pages 2.4:1 ; 2 360 x 200 3 Pages 1.35:1 ; 3 360 x 400 1 Page 2.7:1 ; 4 320 x 240 3 Pages 1:1 ; 5 320 x 480 1 Page 2:1 ; 6 360 x 240 3 Pages 1.125:1 ; 7 360 x 480 1 Page 2.25:1 ; ; -------------------- The Legal Stuff ------------------ ; ; No warranty, either written or implied, is made as to ; the accuracy and usability of this code product. Use ; at your own risk. Batteries not included. Pepperoni ; and extra cheese available for an additional charge. ; ; ----------------------- The Author -------------------- ; ; Matt Pritchard is a paid programmer who'd rather be ; writing games. He can be reached at: P.O. Box 140264, ; Irving, TX 75014 USA. Michael Abrash is a living ; god, who now works for Bill Gates (Microsoft). ; ; -------------------- Revision History ----------------- ; 4-12-93: v1.02 - SET_POINT & READ_POINT now saves DI ; SET_MODEX now saves SI ; 5-3-93: v1.04 - added LOAD_DAC_REGISTERS and ; READ_DAC_REGISTERS. Expanded CLR Macro ; to handle multiple registers ; PAGE 255, 132 .MODEL Huge ;.286 ; ===== MACROS ===== ; Macro to OUT a 16 bit value to an I/O port OUT_16 MACRO Register, Value IFDIFI , ; If DX not setup MOV DX, Register ; then Select Register ENDIF IFDIFI , ; If AX not setup MOV AX, Value ; then Get Data Value ENDIF OUT DX, AX ; Set I/O Register(s) ENDM ; Macro to OUT a 8 bit value to an I/O Port OUT_8 MACRO Register, Value IFDIFI , ; If DX not setup MOV DX, Register ; then Select Register ENDIF IFDIFI , ; If AL not Setup MOV AL, Value ; then Get Data Value ENDIF OUT DX, AL ; Set I/O Register ENDM ; macros to PUSH and POP multiple registers ; PUSHx MACRO R1, R2, R3, R4, R5, R6, R7, R8 ; IFNB ; PUSH R1 ; Save R1 ; PUSHx R2, R3, R4, R5, R6, R7, R8 ; ENDIF ; ENDM ; ; POPx MACRO R1, R2, R3, R4, R5, R6, R7, R8 ; IFNB ; POP R1 ; Restore R1 ; POPx R2, R3, R4, R5, R6, R7, R8 ; ENDIF ; ENDM ; Macro to Clear Registers to 0 ; CLR MACRO Register, R2, R3, R4;, R5, R6 ; IFNB ; XOR Register, Register ; Set Register = 0 ; CLR R2, R3, R4;, R5, R6 ; ENDIF ; ENDM ; Macros to Decrement Counter & Jump on Condition LOOPx MACRO Register, Destination DEC Register ; Counter-- JNZ Destination ; Jump if not 0 ENDM LOOPjz MACRO Register, Destination DEC Register ; Counter-- JZ Destination ; Jump if 0 ENDM ; ===== General Constants ===== False EQU 0 True EQU -1 nil EQU 0 b EQU BYTE PTR w EQU WORD PTR d EQU DWORD PTR o EQU OFFSET f EQU FAR PTR s EQU SHORT ?x1 EQU ; ===== 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 PIXEL_PAN_REG EQU 033h ; Attrib Index: Pixel Pan Reg MAP_MASK EQU 002h ; Sequ Index: Write Map Mask reg READ_MAP EQU 004h ; GC Index: Read Map Register START_DISP_HI EQU 00Ch ; CRTC Index: Display Start Hi START_DISP_LO EQU 00Dh ; CRTC Index: Display Start Lo MAP_MASK_PLANE1 EQU 00102h ; Map Register + Plane 1 MAP_MASK_PLANE2 EQU 01102h ; Map Register + Plane 1 ALL_PLANES_ON EQU 00F02h ; Map Register + All Bit Planes CHAIN4_OFF EQU 00604h ; Chain 4 mode Off ASYNC_RESET EQU 00100h ; (A)synchronous Reset SEQU_RESTART EQU 00300h ; Sequencer Restart LATCHES_ON EQU 00008h ; Bit Mask + Data from Latches LATCHES_OFF EQU 0FF08h ; Bit Mask + Data from CPU VERT_RETRACE EQU 08h ; INPUT_1: Vertical Retrace Bit PLANE_BITS EQU 03h ; Bits 0-1 of Xpos = Plane # ALL_PLANES EQU 0Fh ; All Bit Planes Selected CHAR_BITS EQU 0Fh ; Bits 0-3 of Character Data GET_CHAR_PTR EQU 01130h ; VGA BIOS Func: Get Char Set ROM_8x8_Lo EQU 03h ; ROM 8x8 Char Set Lo Pointer ROM_8x8_Hi EQU 04h ; ROM 8x8 Char Set Hi Pointer ; Constants Specific for these routines NUM_MODES EQU 8 ; # of Mode X Variations ; Specific Mode Data Table format... Mode_Data_Table STRUC M_MiscR DB ? ; Value of MISC_OUTPUT register M_Pages DB ? ; Maximum Possible # of pages M_XSize DW ? ; X Size Displayed on screen M_YSize DW ? ; Y Size Displayed on screen M_XMax DW ? ; Maximum Possible X Size M_YMax DW ? ; Maximum Possible Y Size M_CRTC DW ? ; Table of CRTC register values Mode_Data_Table ENDS ; ===== DGROUP STORAGE NEEDED (42 BYTES) ===== .DATA? SCREEN_WIDTH DW 0 ; Width of a line in Bytes SCREEN_HEIGHT DW 0 ; Vertical Height in Pixels LAST_PAGE DW 0 ; # of Display Pages PAGE_ADDR DW 4 DUP (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 CHARSET_LOW DW 0, 0 ; Far Ptr to Char Set: 0-127 CHARSET_HI DW 0, 0 ; Far Ptr to Char Set: 128-255 .CODE ; ===== DATA TABLES ===== ; Data Tables, Put in Code Segment for Easy Access ; (Like when all the other Segment Registers are in ; use!!) and reduced DGROUP requirements... ; Bit Mask Tables for Left/Right/Character Masks Left_Clip_Mask DB 0FH, 0EH, 0CH, 08H Right_Clip_Mask DB 01H, 03H, 07H, 0FH ; Bit Patterns for converting character fonts Char_Plane_Data DB 00H,08H,04H,0CH,02H,0AH,06H,0EH DB 01H,09H,05H,0DH,03H,0BH,07H,0FH ; CRTC Register Values for Various Configurations MODE_Single_Line: ; CRTC Setup Data for 400/480 Line modes DW 04009H ; Cell Height (1 Scan Line) DW 00014H ; Dword Mode off DW 0E317H ; turn on Byte Mode DW nil ; End of CRTC Data for 400/480 Line Mode MODE_Double_Line: ; CRTC Setup Data for 200/240 Line modes DW 04109H ; Cell Height (2 Scan Lines) DW 00014H ; Dword Mode off DW 0E317H ; turn on Byte Mode DW nil ; End of CRTC Data for 200/240 Line Mode MODE_320_Wide: ; CRTC Setup Data for 320 Horz Pixels DW 05F00H ; Horz total DW 04F01H ; Horz Displayed DW 05002H ; Start Horz Blanking DW 08203H ; End Horz Blanking DW 05404H ; Start H Sync DW 08005H ; End H Sync DW nil ; End of CRTC Data for 320 Horz pixels MODE_360_Wide: ; CRTC Setup Data for 360 Horz Pixels 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 nil ; End of CRTC Data for 360 Horz pixels MODE_200_Tall: MODE_400_Tall: ; CRTC Setup Data for 200/400 Line modes DW 0BF06H ; Vertical Total DW 01F07H ; Overflow DW 09C10H ; V Sync Start DW 08E11H ; V Sync End/Prot Cr0 Cr7 DW 08F12H ; Vertical Displayed DW 09615H ; V Blank Start DW 0B916H ; V Blank End DW nil ; End of CRTC Data for 200/400 Lines MODE_240_Tall: MODE_480_Tall: ; CRTC Setup Data for 240/480 Line modes DW 00D06H ; Vertical Total DW 03E07H ; Overflow DW 0EA10H ; V Sync Start DW 08C11H ; V Sync End/Prot Cr0 Cr7 DW 0DF12H ; Vertical Displayed DW 0E715H ; V Blank Start DW 00616H ; V Blank End DW nil ; End of CRTC Data for 240/480 Lines ; Table of Display Mode Tables MODE_TABLE: DW o MODE_320x200, o MODE_320x400 DW o MODE_360x200, o MODE_360x400 DW o MODE_320x240, o MODE_320x480 DW o MODE_360x240, o MODE_360x480 ; Table of Display Mode Components 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 DW o MODE_320_Wide, o MODE_200_Tall DW o MODE_Double_Line, nil 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 DW o MODE_320_Wide, o MODE_400_Tall DW o MODE_Single_Line, nil 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 DW o MODE_360_Wide, o MODE_240_Tall DW o MODE_Double_Line , nil 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 DW o MODE_360_Wide, o MODE_480_Tall DW o MODE_Single_Line , nil 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 o MODE_320_Wide, o MODE_240_Tall DW o MODE_Double_Line, nil 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 o MODE_320_WIDE, o MODE_480_Tall DW o MODE_Single_Line, nil 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 DW o MODE_360_Wide, MODE_200_Tall DW o MODE_Double_Line, nil 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 DW o MODE_360_Wide, MODE_400_Tall DW o MODE_Single_Line, nil ; ===== MODE X SETUP ROUTINES ===== ;====================================================== ;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) ; ; ENTRY: ModeType = Desired Screen Resolution (0-7) ; ; 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 ; ; EXIT: AX = Success Flag: >0 = Failure / 0 = Success ; SVM_STACK STRUC SVM_Table DW ? ; Offset of Mode Info Table DW ?x1 ; DI, SI, DS, BP DW ?x1 ; DI, SI, DS, BP DW ?x1 ; DI, SI, DS, BP DW ?x1 ; DI, SI, DS, BP DD ? ; Caller SVM_Pages DW ? ; # of Screen Pages desired SVM_Ysize DW ? ; Vertical Screen Size Desired SVM_Xsize DW ? ; Horizontal Screen Size Desired SVM_Mode DW ? ; Display Resolution Desired SVM_STACK ENDS PUBLIC SET_VGA_MODEX SET_VGA_MODEX PROC FAR ;PUSHx BP, DS, SI, DI ; Preserve Important Registers push bp push ds push si push di SUB SP, 2 ; Allocate workspace 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_BadModeSetup1 ; If Not, Error out SHL BX, 1 ; Scale BX MOV SI, w MODE_TABLE[BX] ; CS:SI -> Mode Info MOV [BP].SVM_Table, SI ; Save ptr for later use ; Check # of Requested Display Pages MOV CX, [BP].SVM_Pages ; Get # of Requested Pages ;CLR CH ; Set Hi Word = 0! mov ch,0 ; Set Hi Word = 0! CMP CL, CS:[SI].M_Pages ; Check # Pages for mode JA @SVM_BadModeSetup2 ; Report Error if too Many Pages JCXZ @SVM_BadModeSetup3 ; Report Error if 0 Pages ; 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_BadModeSetup4 ; Report Error if too small CMP AX, CS:[SI].M_XMax ; Check against Max X JA @SVM_BadModeSetup5 ; 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_BadModeSetup6 ; Report Error if too small CMP BX, CS:[SI].M_YMax ; Check against Max Y JA @SVM_BadModeSetup7 ; Report Error if too big ; Enough memory to Fit it all? SHR AX, 1 ; # of Bytes:Line = XSize/4 SHR AX, 1 ; # of Bytes:Line = XSize/4 MUL CX ; AX = Bytes/Line * Pages MUL BX ; DX:AX = Total VGA mem needed JNO @SVM_Continue ; Exit if Total Size > 256K DEC DX ; Was it Exactly 256K??? OR DX, AX ; (DX = 1, AX = 0000) JZ @SVM_Continue ; if so, it's valid... jmp @SVM_Continue;0000 @SVM_BadModeSetup: mov ax,8 ; Return Value = False JMP @SVM_Exit ; Normal Exit @SVM_BadModeSetup1: mov ax,1 ; Return Value = False JMP @SVM_Exit ; Normal Exit @SVM_BadModeSetup2: mov ax,2 ; Return Value = False JMP @SVM_Exit ; Normal Exit @SVM_BadModeSetup3: mov ax,3 ; Return Value = False JMP @SVM_Exit ; Normal Exit @SVM_BadModeSetup4: mov ax,4 ; Return Value = False JMP @SVM_Exit ; Normal Exit @SVM_BadModeSetup5: mov ax,5 ; Return Value = False JMP @SVM_Exit ; Normal Exit @SVM_BadModeSetup6: mov ax,6 ; Return Value = False JMP @SVM_Exit ; Normal Exit @SVM_BadModeSetup7: mov ax,7 ; 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 OUT_8 MISC_OUTPUT, CS:[SI].M_MiscR ; Set New Timing/Size OUT_16 SC_INDEX, SEQU_RESTART ; Restart Sequencer ... OUT_8 CRTC_INDEX, 11H ; Select Vert Retrace End Register 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 ; Load Tables of CRTC Parameters from List of Tables @SVM_Setup_Table: MOV DI, CS:[SI] ; Get Pointer to CRTC Data Tbl ADD SI, 2 ; Point to next Ptr Entry OR DI, DI ; A nil Ptr means that we have JZ @SVM_Set_Data ; finished CRTC programming @SVM_Setup_CRTC: MOV AX, CS:[DI] ; Get CRTC Data from Table ADD DI, 2 ; Advance Pointer OR AX, AX ; At End of Data Table? JZ @SVM_Setup_Table ; If so, Exit & get next Table OUT DX, AX ; Reprogram VGA CRTC reg JMP s @SVM_Setup_CRTC ; Process Next Table Entry ; Initialize Page & Scroll info, DI = 0 @SVM_Set_Data: MOV DISPLAY_PAGE, DI ; Display Page = 0 MOV ACTIVE_PAGE, DI ; Active Page = 0 MOV CURRENT_PAGE, DI ; Current Page (Offset) = 0 MOV CURRENT_XOFFSET, DI ; Horz Scroll Index = 0 MOV CURRENT_YOFFSET, DI ; Vert Scroll Index = 0 MOV CURRENT_MOFFSET, DI ; 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 MOV SI, [BP].SVM_Table ; 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 SHR AX, 1 ; Bytes = Pixels / 4 SHR AX, 1 ; 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 Height 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 Height 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 LOOPx CX, @SVM_Set_Pages ; Loop until all Pages Set ; Clear VGA Memory OUT_16 SC_INDEX, ALL_PLANES_ON ; Select All Planes LES DI, d CURRENT_PAGE ; -> Start of VGA memory mov ax,0 ; AX = 0 CLD ; Block Xfer Forwards MOV CX, 8000H ; 32K * 4 * 2 = 256K REP STOSW ; Clear dat memory! ; Setup Font Pointers MOV BH, ROM_8x8_Lo ; Ask for 8x8 Font, 0-127 MOV AX, GET_CHAR_PTR ; Service to Get Pointer INT 10h ; Call VGA BIOS MOV CHARSET_LOW, BP ; Save Char Set Offset MOV CHARSET_LOW+2, ES ; Save Char Set Segment MOV BH, ROM_8x8_Hi ; Ask for 8x8 Font, 128-255 MOV AX, GET_CHAR_PTR ; Service to Get Pointer INT 10h ; Call VGA BIOS MOV CHARSET_HI, BP ; Save Char Set Offset MOV CHARSET_HI+2, ES ; Save Char Set Segment MOV AX, True ; Return Success Code @SVM_EXIT: ADD SP, 2 ; Deallocate workspace ;POPx DI, SI, DS, BP ; Restore Saved Registers pop di pop si pop ds pop bp RET 8 ; Exit & Clean Up Stack SET_VGA_MODEX ENDP ;================== ;SET_MODEX% (Mode%) ;================== ; ; Quickie Mode Set - Sets Up Mode X to Default Configuration ; ; ENTRY: ModeType = Desired Screen Resolution (0-7) ; (See SET_VGA_MODEX for list) ; ; EXIT: AX = Success Flag: 0 = Failure / -1= Success ; SM_STACK STRUC DW ?,? ; BP, SI DD ? ; Caller SM_Mode DW ? ; Desired Screen Resolution SM_STACK ENDS PUBLIC SET_MODEX SET_MODEX PROC FAR ;PUSHx BP, SI ; Preserve Important registers push bp push si MOV BP, SP ; Set up Stack Frame mov ax,0 ; Assume Failure MOV BX, [BP].SM_Mode ; Get Desired Mode # CMP BX, NUM_MODES ; Is it a Valid Mode #? JAE @SMX_Exit ; If Not, don't Bother PUSH BX ; Push Mode Parameter SHL BX, 1 ; Scale BX to word Index MOV SI, w MODE_TABLE[BX] ; CS:SI -> Mode Info PUSH CS:[SI].M_XSize ; Push Default X Size PUSH CS:[SI].M_Ysize ; Push Default Y size MOV AL, CS:[SI].M_Pages ; Get Default # of Pages mov ah,0 ; Hi Byte = 0 PUSH AX ; Push # Pages CALL f SET_VGA_MODEX ; Set up Mode X! @SMX_Exit: ;POPx SI, BP ; Restore Registers pop si pop bp RET 2 ; Exit & Clean Up Stack SET_MODEX ENDP ; ===== BASIC GRAPHICS PRIMITIVES ===== ;============================ ;CLEAR_VGA_SCREEN (ColorNum%) ;============================ ; ; Clears the active display page ; ; ENTRY: ColorNum = Color Value to fill the page with ; ; EXIT: No meaningful values returned ; 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 ;PUSHx BP, DI ; Preserve Important Registers push bp 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 ; Divide by 2 for Words REP STOSW ; Block Fill VGA memory ;POPx DI, BP ; Restore Saved Registers pop di pop bp RET 2 ; Exit & Clean Up Stack CLEAR_VGA_SCREEN ENDP ;=================================== ;SET_POINT (Xpos%, Ypos%, ColorNum%) ;=================================== ; ; Plots a single Pixel on the active display page ; ; ENTRY: Xpos = X position to plot pixel at ; Ypos = Y position to plot pixel at ; ColorNum = Color to plot pixel with ; ; EXIT: No meaningful values returned ; SP_STACK STRUC DW ?,? ; BP, DI DD ? ; Caller SETP_Color DB ?,? ; Color of Point to Plot SETP_Ypos DW ? ; Y pos of Point to Plot SETP_Xpos DW ? ; X pos of Point to Plot SP_STACK ENDS PUBLIC SET_POINT SET_POINT PROC FAR ;PUSHx BP, DI ; Preserve Registers push bp push di 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 ; Copy to extract Plane # from SHR BX, 1 ; X offset (Bytes) = Xpos/4 SHR BX, 1 ; 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 OUT_16 SC_Index, AX ; Select Plane MOV AL,[BP].SETP_Color ; Get Pixel Color MOV ES:[DI+BX], AL ; Draw Pixel ;POPx DI, BP ; Restore Saved Registers pop di pop bp RET 6 ; Exit and Clean up Stack SET_POINT ENDP ;========================== ;READ_POINT% (Xpos%, Ypos%) ;========================== ; ; Read the color of a pixel from the Active Display Page ; ; ENTRY: Xpos = X position of pixel to read ; Ypos = Y position of pixel to read ; ; EXIT: AX = Color of Pixel at (Xpos, Ypos) ; RP_STACK STRUC DW ?,? ; BP, DI DD ? ; Caller RP_Ypos DW ? ; Y pos of Point to Read RP_Xpos DW ? ; X pos of Point to Read RP_STACK ENDS PUBLIC READ_POINT READ_POINT PROC FAR ;PUSHx BP, DI ; Preserve Registers push bp push di 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, 1 ; X offset (Bytes) = Xpos/4 SHR BX, 1 ; 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 # OUT_16 GC_INDEX, AX ; Select Plane to read in mov ah,0 ; Clear Return Value Hi byte MOV AL, ES:[DI+BX] ; Get Color of Pixel ;POPx DI, BP ; Restore Saved Registers pop di pop bp RET 4 ; Exit and Clean up Stack READ_POINT ENDP ;====================================================== ;FILL_BLOCK (Xpos1%, Ypos1%, Xpos2%, Ypos2%, ColorNum%) ;====================================================== ; ; Fills a rectangular block on the active display Page ; ; ENTRY: Xpos1 = Left X position of area to fill ; Ypos1 = Top Y position of area to fill ; Xpos2 = Right X position of area to fill ; Ypos2 = Bottom Y position of area to fill ; ColorNum = Color to fill area with ; ; EXIT: No meaningful values returned ; FB_STACK STRUC DW ?x1 ; DS, DI, SI, BP DW ?x1 ; DS, DI, SI, BP DW ?x1 ; DS, DI, SI, BP DW ?x1 ; DS, DI, SI, BP DD ? ; Caller FB_Color DB ?,? ; Fill Color FB_Ypos2 DW ? ; Y pos of Lower Right Pixel FB_Xpos2 DW ? ; X pos of Lower Right Pixel FB_Ypos1 DW ? ; Y pos of Upper Left Pixel FB_Xpos1 DW ? ; X pos of Upper Left Pixel FB_STACK ENDS PUBLIC FILL_BLOCK FILL_BLOCK PROC FAR ;PUSHx BP, DS, SI, DI ; Preserve Important Registers push bp push ds push si push di 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, 1 ; DX/4 = Bytes into Line SHR DX, 1 ; DX/4 = Bytes into Line ADD DI, DX ; DI = Addr of Upper-Left Corner MOV CX, BX ; CX = X2 (Pixel Position) SHR CX, 1 ; CX/4 = Bytes into Line SHR CX, 1 ; 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_L_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) LOOPjz DX, @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) LOOPx DX, @FB_LEFT_LOOP ; loop until left strip is drawn @FB_LEFT_CONT: INC DI ; Point to Middle (or Right) Block DEC CX ; Reset CX instead of JMP @FB_RIGHT @FB_L_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_R_EDGE_FLUSH ; Hey, add to middle ; Draw "Right Edge" vertical strip of 1-3 pixels... OUT_8 SC_Data, Right_Clip_Mask[SI] ; 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) LOOPjz DX, @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) LOOPx DX, @FB_RIGHT_LOOP ; loop until left strip is drawn @FB_RIGHT_CONT: DEC CX ; Minus 1 for Middle bands JZ @FB_EXIT ; Uh.. no Middle bands... @FB_R_EDGE_FLUSH: ; DI = Addr of Upper Left block to fill ; CX = # of Bands to fill in (width) OUT_8 SC_Data, ALL_PLANES ; Write to All Planes MOV DX, SCREEN_WIDTH ; DX = DI Increment 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 LOOPx SI, @FB_MIDDLE_LOOP ; Loop until all lines drawn JMP s @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 OUT_8 SC_Data, 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) LOOPjz CX, @FB_EXIT ; Exit loop if all Lines Drawn MOV ES:[DI], AL ; Fill in Pixels ADD DI, BX ; Point to Next Line (Below) LOOPx CX, @FB_ONE_LOOP ; loop until left strip is drawn @FB_EXIT: ;POPx DI, SI, DS, BP ; Restore Saved Registers pop di pop si pop ds pop bp RET 10 ; Exit and Clean up Stack FILL_BLOCK ENDP ;===================================================== ;DRAW_LINE (Xpos1%, Ypos1%, Xpos2%, Ypos2%, ColorNum%) ;===================================================== ; ; Draws a Line on the active display page ; ; ENTRY: Xpos1 = X position of first point on line ; Ypos1 = Y position of first point on line ; Xpos2 = X position of last point on line ; Ypos2 = Y position of last point on line ; ColorNum = Color to draw line with ; ; EXIT: No meaningful values returned ; DL_STACK STRUC DW ?x1 ; DI, SI, BP DW ?x1 ; DI, SI, BP DW ?x1 ; DI, SI, BP DD ? ; Caller DL_ColorF DB ?,? ; Line Draw Color DL_Ypos2 DW ? ; Y pos of last point DL_Xpos2 DW ? ; X pos of last point DL_Ypos1 DW ? ; Y pos of first point DL_Xpos1 DW ? ; X pos of first point DL_STACK ENDS PUBLIC DRAW_LINE DRAW_LINE PROC FAR ;PUSHx BP, SI, DI ; Preserve Important Registers push bp 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 Line 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 offset into Page MOV AX, SI ; Get Left edge, 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, Save X2 AND DI, PLANE_BITS ; Mask out Row # MOV BH, Right_Clip_Mask[DI] ; Get Right Edge Mask byte SHR AX, 1 ; Get X1 Byte # (=X1/4) SHR CX, 1 ; Get X2 Byte # (=X2/4) SHR AX, 1 ; Get X1 Byte # (=X1/4) SHR CX, 1 ; 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 ; jump if longer than one segment AND BL, BH ; otherwise, merge 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 Segments JZ @DL_XRSEG ; If no middle segments.... ; Draw Middle Segments OUT_8 DX, ALL_PLANES ; Write to ALL Planes MOV AL, BL ; Get Color from BL REP STOSB ; Draw Middle (4 Pixel) Segments @DL_XRSEG: OUT_8 DX, BH ; Select Planes for Right Clip Mask MOV AL, BL ; Get Color Value STOSB ; Draw Right (1-4) Pixels JMP s @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 Height (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, 1 ; DI = Xpos/4 SHR DI, 1 ; 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 # OUT_16 SC_Index, 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 LOOPjz SI, @DL_EXIT ; Lines--, Exit if done MOV ES:[DI], AL ; Draw Single Pixel ADD DI, BX ; Point to Next Line LOOPx SI, @DL_VLoop ; Lines--, 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, 1 ; /4 = Byte Offset into Line SHR AX, 1 ; /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 AH, AL ; Plane # in AH MOV AL, MAP_MASK ; Select Plane Register OUT_16 SC_Index, 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 negative, go left JMP @DL_DrawRight ; Draw Line that slopes right @DL_DrawLeft: NEG CX ; abs(Delta_X) CMP CX, DX ; is Delta_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: mov ax,0 ; zero low word of Delta_Y * 10000h SUB AX, DX ; DX:AX <- DX * 0FFFFh SBB DX, 0 ; include carry DIV CX ; divide by Delta_X MOV SI, BX ; SI = Accumulator MOV BX, AX ; BX = Add fraction 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 LOOPjz CX, @DL_SLLExit ; Delta_X--, 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, back on 0 1 2 CMP AL, 87h ; wrap?, if AL <88 then Carry set ADC DI, 0 ; Adjust Address: DI = DI + Carry OUT DX, AL ; Set up New Bit Plane mask MOV ES:[DI], AH ; set pixel LOOPjz CX, @DL_SLLExit ; Delta_X--, Exit if done 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, back on 0 1 2 CMP AL, 87h ; Wrap?, if AL <88 then Carry set ADC DI, 0 ; Adjust Address: DI = DI + Carry OUT DX, AL ; Set up New Bit Plane mask JMP s @DL_SLLLoop ; loop until done @DL_SLLExit: JMP @DL_EXIT2 ; and exit ; Draw a steep line to the left in Mode X @DL_SteepLeft: mov ax,0 ; 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 = Accumulator MOV BX, AX ; BX = Add Fraction 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 LOOPjz CX, @DL_STLExit ; Delta_Y--, Exit if done ADD SI, BX ; add numerator to accumulator JNC @DL_STLnc2 ; No carry, just move down! DEC DI ; Move Left one addr ROR AL, 1 ; Move Left one plane, back on 0 1 2 CMP AL, 87h ; Wrap?, if AL <88 then Carry set ADC DI, 0 ; Adjust Address: DI = DI + Carry OUT DX, AL ; Set up New Bit Plane mask @DL_STLnc2: ADD DI, BP ; advance to next line. MOV ES:[DI], AH ; set pixel LOOPjz CX, @DL_STLExit ; Delta_Y--, Exit if done ADD SI, BX ; add numerator to accumulator JNC @DL_STLnc3 ; No carry, just move down! DEC DI ; Move Left one addr ROR AL, 1 ; Move Left one plane, back on 0 1 2 CMP AL, 87h ; Wrap?, if AL <88 then Carry set ADC DI, 0 ; Adjust Address: DI = DI + Carry OUT DX, AL ; Set up New Bit Plane mask @DL_STLnc3: ADD DI, BP ; advance to next line. JMP s @DL_STLLoop ; Loop until done @DL_STLExit: JMP @DL_EXIT2 ; and exit ; Draw a line that goes to the Right... @DL_DrawRight: CMP CX, DX ; is Delta_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: mov ax,0 ; zero low word of Delta_Y * 10000h SUB AX, DX ; DX:AX <- DX * 0FFFFh SBB DX, 0 ; include carry DIV CX ; divide by Delta_X MOV SI, BX ; SI = Accumulator MOV BX, AX ; BX = Add Fraction 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 is set up LOOPjz CX, @DL_SLRExit ; Delta_X--, 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 addr if Plane = 0 CMP AL, 12h ; Wrap? if AL >12 then Carry not set ADC DI, 0 ; Adjust Address: DI = DI + Carry OUT DX, AL ; Set up New Bit Plane mask MOV ES:[DI], AH ; set pixel LOOPjz CX, @DL_SLRExit ; Delta_X--, Exit if done.. 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 addr if Plane = 0 CMP AL, 12h ; Wrap? if AL >12 then Carry not set ADC DI, 0 ; Adjust Address: DI = DI + Carry OUT DX, AL ; Set up New Bit Plane mask JMP s @DL_SLRLoop ; loop till done @DL_SLRExit: JMP @DL_EXIT2 ; and exit ; Draw a Steep line to the Right in Mode X @DL_SteepRight: mov ax,0 ; 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 = Accumulator MOV BX, AX ; BX = Add Fraction 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 is set up LOOPjz CX, @DL_EXIT2 ; Delta_Y--, Exit if Done ADD SI, BX ; add numerator to accumulator JNC @STRnc2 ; if no carry then just go down... ROL AL, 1 ; Move Right one addr if Plane = 0 CMP AL, 12h ; Wrap? if AL >12 then Carry not set ADC DI, 0 ; Adjust Address: DI = DI + Carry OUT DX, AL ; Set up New Bit Plane mask @STRnc2: ADD DI, BP ; advance to next line. MOV ES:[DI], AH ; set pixel LOOPjz CX, @DL_EXIT2 ; Delta_Y--, Exit if Done ADD SI, BX ; add numerator to accumulator JNC @STRnc3 ; if no carry then just go down... ROL AL, 1 ; Move Right one addr if Plane = 0 CMP AL, 12h ; Wrap? if AL >12 then Carry not set ADC DI, 0 ; Adjust Address: DI = DI + Carry OUT DX, AL ; Set up New Bit Plane mask @STRnc3: ADD DI, BP ; advance to next line. JMP s @STRLoop ; loop till done @DL_EXIT2: ;POPx DI, SI, BP ; Restore Saved Registers pop di pop si pop bp RET 10 ; Exit and Clean up Stack DRAW_LINE ENDP ; ===== DAC COLOR REGISTER ROUTINES ===== ;================================================= ;SET_DAC_REGISTER (Register%, Red%, Green%, Blue%) ;================================================= ; ; Sets a single (RGB) Vga Palette Register ; ; ENTRY: Register = The DAC # to modify (0-255) ; Red = The new Red Intensity (0-63) ; Green = The new Green Intensity (0-63) ; Blue = The new Blue Intensity (0-63) ; ; EXIT: No meaningful values returned ; 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 OUT_8 DX, [BP].SDR_Red ; Set Red Intensity OUT_8 DX, [BP].SDR_Green ; Set Green Intensity OUT_8 DX, [BP].SDR_Blue ; Set Blue Intensity POP BP ; Restore Registers RET 8 ; Exit & Clean Up Stack SET_DAC_REGISTER ENDP ;==================================================== ;GET_DAC_REGISTER (Register%, &Red%, &Green%, &Blue%) ;==================================================== ; ; Reads the RGB Values of a single Vga Palette Register ; ; ENTRY: Register = The DAC # to read (0-255) ; Red = Offset to Red Variable in DS ; Green = Offset to Green Variable in DS ; Blue = Offset to Blue Variable in DS ; ; EXIT: The values of the integer variables Red, ; Green, and Blue are set to the values ; taken from the specified DAC register. ; 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 mov ax,0 ; 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 ;=========================================================== ;LOAD_DAC_REGISTERS (SEG PalData, StartReg%, EndReg%, Sync%) ;=========================================================== ; ; Sets a Block of Vga Palette Registers ; ; ENTRY: PalData = Far Pointer to Block of palette data ; StartReg = First Register # in range to set (0-255) ; EndReg = Last Register # in Range to set (0-255) ; Sync = Wait for Vertical Retrace Flag (Boolean) ; ; EXIT: No meaningful values returned ; ; NOTES: PalData is a linear array of 3 byte Palette values ; in the order: Red (0-63), Green (0-63), Blue (0-63) ; LDR_STACK STRUC DW ?x1 ; BP, DS, SI DW ?x1 ; BP, DS, SI DW ?x1 ; BP, DS, SI DD ? ; Caller LDR_Sync DW ? ; Vertical Sync Flag LDR_EndReg DB ?,? ; Last Register # LDR_StartReg DB ?,? ; First Register # LDR_PalData DD ? ; Far Ptr to Palette Data LDR_STACK ENDS PUBLIC LOAD_DAC_REGISTERS LOAD_DAC_REGISTERS PROC FAR ;PUSHx BP, DS, SI ; Save Registers push bp push ds push si mov BP, SP ; Set up Stack Frame mov AX, [BP].LDR_Sync ; Get Vertical Sync Flag or AX, AX ; is Sync Flag = 0? jz @LDR_Load ; if so, skip call call f SYNC_DISPLAY ; wait for vsync ; Determine register #'s, size to copy, etc @LDR_Load: lds SI, [BP].LDR_PalData ; DS:SI -> Palette Data mov DX, DAC_WRITE_ADDR ; DAC register # selector mov ax,0, BX ; Clear for byte loads mov AL, [BP].LDR_StartReg ; Get Start Register mov BL, [BP].LDR_EndReg ; Get End Register sub BX, AX ; BX = # of DAC registers -1 inc BX ; BX = # of DAC registers mov CX, BX ; CX = # of DAC registers add CX, BX ; CX = " " * 2 add CX, BX ; CX = " " * 3 cld ; Block OUTs forward out DX, AL ; set up correct register # ; Load a block of DAC Registers mov DX, PEL_DATA_REG ; Dac Data Register ;rep outsb ; block set DAC registers ;POPx SI, DS, BP ; Restore Registers pop si pop ds pop bp ret 10 ; Exit & Clean Up Stack LOAD_DAC_REGISTERS ENDP ;==================================================== ;READ_DAC_REGISTERS (SEG PalData, StartReg%, EndReg%) ;==================================================== ; ; Reads a Block of Vga Palette Registers ; ; ENTRY: PalData = Far Pointer to block to store palette data ; StartReg = First Register # in range to read (0-255) ; EndReg = Last Register # in Range to read (0-255) ; ; EXIT: No meaningful values returned ; ; NOTES: PalData is a linear array of 3 byte Palette values ; in the order: Red (0-63), Green (0-63), Blue (0-63) ; RDR_STACK STRUC DW ?x1 ; BP, ES, DI DW ?x1 ; BP, ES, DI DW ?x1 ; BP, ES, DI DD ? ; Caller RDR_EndReg DB ?,? ; Last Register # RDR_StartReg DB ?,? ; First Register # RDR_PalData DD ? ; Far Ptr to Palette Data RDR_STACK ENDS PUBLIC READ_DAC_REGISTERS READ_DAC_REGISTERS PROC FAR ;PUSHx BP, ES, DI ; Save Registers push bp push es push di mov BP, SP ; Set up Stack Frame ; Determine register #'s, size to copy, etc les DI, [BP].RDR_PalData ; ES:DI -> Palette Buffer mov DX, DAC_READ_ADDR ; DAC register # selector mov ax,0, BX ; Clear for byte loads mov AL, [BP].RDR_StartReg ; Get Start Register mov BL, [BP].RDR_EndReg ; Get End Register sub BX, AX ; BX = # of DAC registers -1 inc BX ; BX = # of DAC registers mov CX, BX ; CX = # of DAC registers add CX, BX ; CX = " " * 2 add CX, BX ; CX = " " * 3 cld ; Block INs forward ; Read a block of DAC Registers out DX, AL ; set up correct register # mov DX, PEL_DATA_REG ; Dac Data Register ;rep insb ; block read DAC registers ;POPx DI, ES, BP ; Restore Registers pop di pop es pop bp ret 8 ; Exit & Clean Up Stack READ_DAC_REGISTERS ENDP ; ===== PAGE FLIPPING AND SCROLLING ROUTINES ===== ;========================= ;SET_ACTIVE_PAGE (PageNo%) ;========================= ; ; Sets the active display Page to be used for future drawing ; ; ENTRY: PageNo = Display Page to make active ; (values: 0 to Number of Pages - 1) ; ; EXIT: No meaningful values returned ; 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 ; Exit and Clean up Stack SET_ACTIVE_PAGE ENDP ;================ ;GET_ACTIVE_PAGE% ;================ ; ; Returns the Video Page # currently used for Drawing ; ; ENTRY: No Parameters are passed ; ; EXIT: AX = Current Video Page used for Drawing ; PUBLIC GET_ACTIVE_PAGE GET_ACTIVE_PAGE PROC FAR MOV AX, ACTIVE_PAGE ; Get Active Page # RET ; Exit and Clean up Stack GET_ACTIVE_PAGE ENDP ;=============================== ;SET_DISPLAY_PAGE (DisplayPage%) ;=============================== ; ; Sets the currently visible display page. ; When called this routine syncronizes the display ; to the vertical blank. ; ; ENTRY: PageNo = Display Page to show on the screen ; (values: 0 to Number of Pages - 1) ; ; EXIT: No meaningful values returned ; 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 ; Wait if we are currently in a Vertical Retrace MOV DX, INPUT_1 ; Input Status #1 Register @DP_WAIT0: IN AL, DX ; Get VGA status AND AL, VERT_RETRACE ; In Display mode yet? JNZ @DP_WAIT0 ; If Not, wait for it ; Set the Start Display Address to the new page 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 ; Wait for a Vertical Retrace to smooth out things MOV DX, INPUT_1 ; Input Status #1 Register @DP_WAIT1: IN AL, DX ; Get VGA status AND AL, VERT_RETRACE ; Vertical Retrace Start? JZ @DP_WAIT1 ; If Not, wait for it ; Now Set Display Starting Address @SDP_Exit: POP BP ; Restore Registers RET 2 ; Exit and Clean up Stack SET_DISPLAY_PAGE ENDP ;================= ;GET_DISPLAY_PAGE% ;================= ; ; Returns the Video Page # currently displayed ; ; ENTRY: No Parameters are passed ; ; EXIT: AX = Current Video Page being displayed ; PUBLIC GET_DISPLAY_PAGE GET_DISPLAY_PAGE PROC FAR MOV AX, DISPLAY_PAGE ; Get Display Page # RET ; Exit & Clean Up Stack GET_DISPLAY_PAGE ENDP ;======================================= ;SET_WINDOW (DisplayPage%, 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. ; Also Sets the Display page to simplify combined page ; flipping and scrolling. When called this routine ; syncronizes the display to the vertical blank. ; ; ENTRY: DisplayPage = Display Page to show on the screen ; Xpos = # of pixels to shift screen right ; Ypos = # of lines to shift screen down ; ; EXIT: No meaningful values returned ; SW_STACK STRUC DW ? ; BP DD ? ; Caller SW_Ypos DW ? ; Y pos of UL Screen Corner SW_Xpos DW ? ; X pos of UL Screen Corner SW_Page DW ? ; (new) Display Page 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 BX, [BP].SW_Page ; Get Desired Page # CMP BX, LAST_PAGE ; Is Page # Valid? JAE @SW_Exit ; IF Not, Do Nothing MOV AX, [BP].SW_Ypos ; Get Desired Y Offset CMP AX, MAX_YOFFSET ; Is it Within Limits? JA @SW_Exit ; if not, exit MOV CX, [BP].SW_Xpos ; Get Desired X Offset CMP CX, MAX_XOFFSET ; Is it Within Limits? JA @SW_Exit ; if not, exit ; Compute proper Display start address to use MUL SCREEN_WIDTH ; AX = YOffset * Line Width SHR CX, 1 ; CX / 4 = Bytes into Line SHR CX, 1 ; CX / 4 = Bytes into Line ADD AX, CX ; AX = Offset of Upper Left Pixel MOV CURRENT_MOFFSET, AX ; Save Offset Info MOV DISPLAY_PAGE, BX ; Set Current 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 if we are currently in a Vertical Retrace @SW_WAIT0: IN AL, DX ; Get VGA status AND AL, VERT_RETRACE ; In Display mode yet? JNZ @SW_WAIT0 ; If Not, wait for it ; 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 ; Wait for a Vertical Retrace to smooth out things MOV DX, INPUT_1 ; Input Status #1 Register @SW_WAIT1: IN AL, DX ; Get VGA status AND AL, VERT_RETRACE ; Vertical Retrace Start? JZ @SW_WAIT1 ; If Not, wait for it ; Now Set the Horizontal Pixel Pan values OUT_8 ATTRIB_Ctrl, PIXEL_PAN_REG ; Select Pixel Pan Register MOV AX, [BP].SW_Xpos ; Get Desired X Offset AND AL, 03 ; Get # of Pixels to Pan (0-3) SHL AL, 1 ; Shift for 256 Color Mode OUT DX, AL ; Fine tune the display! @SW_Exit: POP BP ; Restore Saved Registers RET 6 ; Exit and Clean up Stack SET_WINDOW ENDP ;============= ;GET_X_OFFSET% ;============= ; ; Returns the X coordinate of the Pixel currently display ; in the upper left corner of the display ; ; ENTRY: No Parameters are passed ; ; EXIT: AX = Current Horizontal Scroll Offset ; PUBLIC GET_X_OFFSET GET_X_OFFSET PROC FAR MOV AX, CURRENT_XOFFSET ; Get current horz offset RET ; Exit & Clean Up Stack GET_X_OFFSET ENDP ;============= ;GET_Y_OFFSET% ;============= ; ; Returns the Y coordinate of the Pixel currently display ; in the upper left corner of the display ; ; ENTRY: No Parameters are passed ; ; EXIT: AX = Current Vertical Scroll Offset ; PUBLIC GET_Y_OFFSET GET_Y_OFFSET PROC FAR MOV AX, CURRENT_YOFFSET ; Get current vertical offset RET ; Exit & Clean Up Stack GET_Y_OFFSET ENDP ;============ ;SYNC_DISPLAY ;============ ; ; Pauses the computer until the next Vertical Retrace starts ; ; ENTRY: No Parameters are passed ; ; EXIT: No meaningful values returned ; PUBLIC SYNC_DISPLAY SYNC_DISPLAY PROC FAR MOV DX, INPUT_1 ; Input Status #1 Register ; Wait for any current retrace to end @SD_WAIT0: IN AL, DX ; Get VGA status AND AL, VERT_RETRACE ; In Display mode yet? JNZ @SD_WAIT0 ; If Not, wait for it ; Wait for the start of the next vertical retrace @SD_WAIT1: IN AL, DX ; Get VGA status AND AL, VERT_RETRACE ; Vertical Retrace Start? JZ @SD_WAIT1 ; If Not, wait for it RET ; Exit & Clean Up Stack SYNC_DISPLAY ENDP ; ===== TEXT DISPLAY ROUTINES ===== ;================================================== ;GPRINTC (CharNum%, Xpos%, Ypos%, ColorF%, ColorB%) ;================================================== ; ; Draws an ASCII Text Character using the currently selected ; 8x8 font on the active display page. It would be a simple ; exercise to make this routine process variable height fonts. ; ; ENTRY: CharNum = ASCII character # to draw ; Xpos = X position to draw Character at ; Ypos = Y position of to draw Character at ; ColorF = Color to draw text character in ; ColorB = Color to set background to ; ; EXIT: No meaningful values returned ; GPC_STACK STRUC GPC_Width DW ? ; Screen Width-1 GPC_Lines DB ?,? ; Scan lines to Decode GPC_T_SETS DW ? ; Saved Charset Segment GPC_T_SETO DW ? ; Saved Charset Offset DW ?x1 ; DI, SI, DS, BP DW ?x1 ; DS, DI, SI, BP DW ?x1 ; DS, DI, SI, BP DW ?x1 ; DS, DI, SI, BP DD ? ; Caller GPC_ColorB DB ?,? ; Background Color GPC_ColorF DB ?,? ; Text Color GPC_Ypos DW ? ; Y Position to Print at GPC_Xpos DW ? ; X position to Print at GPC_Char DB ?,? ; Character to Print GPC_STACK ENDS PUBLIC GPRINTC GPRINTC PROC FAR ;PUSHx BP, DS, SI, DI ; Preserve Important Registers push bp push ds push si push di SUB SP, 8 ; Allocate WorkSpace on Stack MOV BP, SP ; Set up Stack Frame LES DI, d CURRENT_PAGE ; Point to Active VGA Page MOV AX, SCREEN_WIDTH ; Get Logical Line Width MOV BX, AX ; BX = Screen Width DEC BX ; = Screen Width-1 MOV [BP].GPC_Width, BX ; Save for later use MUL [BP].GPC_Ypos ; Start of Line = Ypos * Width ADD DI, AX ; DI -> Start of Line Ypos MOV AX, [BP].GPC_Xpos ; Get Xpos of Character MOV CX, AX ; Save Copy of Xpos SHR AX, 1 ; Bytes into Line = Xpos/4 SHR AX, 1 ; Bytes into Line = Xpos/4 ADD DI, AX ; DI -> (Xpos, Ypos) ;Get Source ADDR of Character Bit Map & Save MOV AL, [BP].GPC_Char ; Get Character # TEST AL, 080h ; Is Hi Bit Set? JZ @GPC_LowChar ; Nope, use low char set ptr AND AL, 07Fh ; Mask Out Hi Bit MOV BX, CHARSET_HI ; BX = Char Set Ptr:Offset MOV DX, CHARSET_HI+2 ; DX = Char Set Ptr:Segment JMP s @GPC_Set_Char ; Go Setup Character Ptr @GPC_LowChar: MOV BX, CHARSET_LOW ; BX = Char Set Ptr:Offset MOV DX, CHARSET_LOW+2 ; DX = Char Set Ptr:Segment @GPC_Set_Char: MOV [BP].GPC_T_SETS, DX ; Save Segment on Stack MOV AH, 0 ; Valid #'s are 0..127 SHL AX, 1 ; * 8 Bytes Per Bitmap SHL AX, 1 ; * 8 Bytes Per Bitmap SHL AX, 1 ; * 8 Bytes Per Bitmap ADD BX, AX ; BX = Offset of Selected char MOV [BP].GPC_T_SETO, BX ; Save Offset on Stack AND CX, PLANE_BITS ; Get Plane # MOV CH, ALL_PLANES ; Get Initial Plane mask SHL CH, CL ; And shift into position AND CH, ALL_PLANES ; And mask to lower nibble MOV AL, 04 ; 4-Plane # = # of initial SUB AL, CL ; shifts to align bit mask MOV CL, AL ; Shift Count for SHL ;Get segment of character map OUT_8 SC_Index, MAP_MASK ; Setup Plane selections INC DX ; DX -> SC_Data MOV AL, 08 ; 8 Lines to Process MOV [BP].GPC_Lines, AL ; Save on Stack MOV DS, [BP].GPC_T_SETS ; Point to character set @GPC_DECODE_CHAR_BYTE: MOV SI, [BP].GPC_T_SETO ; Get DS:SI = String MOV BH, [SI] ; Get Bit Map INC SI ; Point to Next Line MOV [BP].GPC_T_SETO, SI ; And save new Pointer... mov ax,0 ; Clear AX ;mov bl,0 ; Clear BL mov bl,0 ROL BX, CL ; BL holds left edge bits MOV SI, BX ; Use as Table Index AND SI, CHAR_BITS ; Get Low Bits MOV AL, Char_Plane_Data[SI] ; Get Mask in AL JZ @GPC_NO_LEFT1BITS ; Skip if No Pixels to set MOV AH, [BP].GPC_ColorF ; Get Foreground Color OUT DX, AL ; Set up Screen Mask MOV ES:[DI], AH ; Write Foreground color @GPC_NO_LEFT1BITS: XOR AL, CH ; Invert mask for Background JZ @GPC_NO_LEFT0BITS ; Hey, no need for this MOV AH, [BP].GPC_ColorB ; Get background Color OUT DX, AL ; Set up Screen Mask MOV ES:[DI], AH ; Write Foreground color ;Now Do Middle/Last Band @GPC_NO_LEFT0BITS: INC DI ; Point to next Byte ROL BX, 1 ; Shift 4 bits ROL BX, 1 ; Shift 4 bits ROL BX, 1 ; Shift 4 bits ROL BX, 1 ; Shift 4 bits MOV SI, BX ; Make Lookup Pointer AND SI, CHAR_BITS ; Get Low Bits MOV AL, Char_Plane_Data[SI] ; Get Mask in AL JZ @GPC_NO_MIDDLE1BITS ; Skip if no pixels to set MOV AH, [BP].GPC_ColorF ; Get Foreground Color OUT DX, AL ; Set up Screen Mask MOV ES:[DI], AH ; Write Foreground color @GPC_NO_MIDDLE1BITS: XOR AL, ALL_PLANES ; Invert mask for Background JZ @GPC_NO_MIDDLE0BITS ; Hey, no need for this MOV AH, [BP].GPC_ColorB ; Get background Color OUT DX, AL ; Set up Screen Mask MOV ES:[DI], AH ; Write Foreground color @GPC_NO_MIDDLE0BITS: XOR CH, ALL_PLANES ; Invert Clip Mask CMP CL, 4 ; Aligned by 4? JZ @GPC_NEXT_LINE ; If so, Exit now.. INC DI ; Point to next Byte ROL BX, 1 ; Shift 4 bits ROL BX, 1 ; Shift 4 bits ROL BX, 1 ; Shift 4 bits ROL BX, 1 ; Shift 4 bits MOV SI, BX ; Make Lookup Pointer AND SI, CHAR_BITS ; Get Low Bits MOV AL, Char_Plane_Data[SI] ; Get Mask in AL JZ @GPC_NO_RIGHT1BITS ; Skip if No Pixels to set MOV AH, [BP].GPC_ColorF ; Get Foreground Color OUT DX, AL ; Set up Screen Mask MOV ES:[DI], AH ; Write Foreground color @GPC_NO_RIGHT1BITS: XOR AL, CH ; Invert mask for Background JZ @GPC_NO_RIGHT0BITS ; Hey, no need for this MOV AH, [BP].GPC_ColorB ; Get background Color OUT DX, AL ; Set up Screen Mask MOV ES:[DI], AH ; Write Foreground color @GPC_NO_RIGHT0BITS: DEC DI ; Adjust for Next Line Advance @GPC_NEXT_LINE: ADD DI, [BP].GPC_Width ; Point to Next Line XOR CH, CHAR_BITS ; Flip the Clip mask back DEC [BP].GPC_Lines ; Count Down Lines JZ @GPC_EXIT ; Ok... Done! JMP @GPC_DECODE_CHAR_BYTE ; Again! Hey! @GPC_EXIT: ADD SP, 08 ; Deallocate stack workspace ;POPx DI, SI, DS, BP ; Restore Saved Registers pop di pop si pop ds pop bp RET 10 ; Exit and Clean up Stack GPRINTC ENDP ;========================================== ;TGPRINTC (CharNum%, Xpos%, Ypos%, ColorF%) ;========================================== ; ; Transparently draws an ASCII Text Character using the ; currently selected 8x8 font on the active display page. ; ; ENTRY: CharNum = ASCII character # to draw ; Xpos = X position to draw Character at ; Ypos = Y position of to draw Character at ; ColorF = Color to draw text character in ; ; EXIT: No meaningful values returned ; TGP_STACK STRUC TGP_Width DW ? ; Screen Width-1 TGP_Lines DB ?,? ; Scan lines to Decode TGP_T_SETS DW ? ; Saved Charset Segment TGP_T_SETO DW ? ; Saved Charset Offset DW ?x1 ; DI, SI, DS, BP DW ?x1 ; DS, DI, SI, BP DW ?x1 ; DS, DI, SI, BP DW ?x1 ; DS, DI, SI, BP DD ? ; Caller TGP_ColorF DB ?,? ; Text Color TGP_Ypos DW ? ; Y Position to Print at TGP_Xpos DW ? ; X position to Print at TGP_Char DB ?,? ; Character to Print TGP_STACK ENDS PUBLIC TGPRINTC TGPRINTC PROC FAR ;PUSHx BP, DS, SI, DI ; Preserve Important Registers push bp push ds push si push di SUB SP, 8 ; Allocate WorkSpace on Stack MOV BP, SP ; Set up Stack Frame LES DI, d CURRENT_PAGE ; Point to Active VGA Page MOV AX, SCREEN_WIDTH ; Get Logical Line Width MOV BX, AX ; BX = Screen Width DEC BX ; = Screen Width-1 MOV [BP].TGP_Width, BX ; Save for later use MUL [BP].TGP_Ypos ; Start of Line = Ypos * Width ADD DI, AX ; DI -> Start of Line Ypos MOV AX, [BP].TGP_Xpos ; Get Xpos of Character MOV CX, AX ; Save Copy of Xpos SHR AX, 1 ; Bytes into Line = Xpos/4 SHR AX, 1 ; Bytes into Line = Xpos/4 ADD DI, AX ; DI -> (Xpos, Ypos) ;Get Source ADDR of Character Bit Map & Save MOV AL, [BP].TGP_Char ; Get Character # TEST AL, 080h ; Is Hi Bit Set? JZ @TGP_LowChar ; Nope, use low char set ptr AND AL, 07Fh ; Mask Out Hi Bit MOV BX, CHARSET_HI ; BX = Char Set Ptr:Offset MOV DX, CHARSET_HI+2 ; DX = Char Set Ptr:Segment JMP s @TGP_Set_Char ; Go Setup Character Ptr @TGP_LowChar: MOV BX, CHARSET_LOW ; BX = Char Set Ptr:Offset MOV DX, CHARSET_LOW+2 ; DX = Char Set Ptr:Segment @TGP_Set_Char: MOV [BP].TGP_T_SETS, DX ; Save Segment on Stack MOV AH, 0 ; Valid #'s are 0..127 SHL AX, 1 ; * 8 Bytes Per Bitmap SHL AX, 1 ; * 8 Bytes Per Bitmap SHL AX, 1 ; * 8 Bytes Per Bitmap ADD BX, AX ; BX = Offset of Selected char MOV [BP].TGP_T_SETO, BX ; Save Offset on Stack AND CX, PLANE_BITS ; Get Plane # MOV CH, ALL_PLANES ; Get Initial Plane mask SHL CH, CL ; And shift into position AND CH, ALL_PLANES ; And mask to lower nibble MOV AL, 04 ; 4-Plane # = # of initial SUB AL, CL ; shifts to align bit mask MOV CL, AL ; Shift Count for SHL ;Get segment of character map OUT_8 SC_Index, MAP_MASK ; Setup Plane selections INC DX ; DX -> SC_Data MOV AL, 08 ; 8 Lines to Process MOV [BP].TGP_Lines, AL ; Save on Stack MOV DS, [BP].TGP_T_SETS ; Point to character set @TGP_DECODE_CHAR_BYTE: MOV SI, [BP].TGP_T_SETO ; Get DS:SI = String MOV BH, [SI] ; Get Bit Map INC SI ; Point to Next Line MOV [BP].TGP_T_SETO, SI ; And save new Pointer... MOV AH, [BP].TGP_ColorF ; Get Foreground Color mov bl,0 ; Clear BL ROL BX, CL ; BL holds left edge bits MOV SI, BX ; Use as Table Index AND SI, CHAR_BITS ; Get Low Bits MOV AL, Char_Plane_Data[SI] ; Get Mask in AL JZ @TGP_NO_LEFT1BITS ; Skip if No Pixels to set OUT DX, AL ; Set up Screen Mask MOV ES:[DI], AH ; Write Foreground color ;Now Do Middle/Last Band @TGP_NO_LEFT1BITS: INC DI ; Point to next Byte ROL BX, 1 ; Shift 4 bits ROL BX, 1 ; Shift 4 bits ROL BX, 1 ; Shift 4 bits ROL BX, 1 ; Shift 4 bits MOV SI, BX ; Make Lookup Pointer AND SI, CHAR_BITS ; Get Low Bits MOV AL, Char_Plane_Data[SI] ; Get Mask in AL JZ @TGP_NO_MIDDLE1BITS ; Skip if no pixels to set OUT DX, AL ; Set up Screen Mask MOV ES:[DI], AH ; Write Foreground color @TGP_NO_MIDDLE1BITS: XOR CH, ALL_PLANES ; Invert Clip Mask CMP CL, 4 ; Aligned by 4? JZ @TGP_NEXT_LINE ; If so, Exit now.. INC DI ; Point to next Byte ROL BX, 1 ; Shift 4 bits ROL BX, 1 ; Shift 4 bits ROL BX, 1 ; Shift 4 bits ROL BX, 1 ; Shift 4 bits MOV SI, BX ; Make Lookup Pointer AND SI, CHAR_BITS ; Get Low Bits MOV AL, Char_Plane_Data[SI] ; Get Mask in AL JZ @TGP_NO_RIGHT1BITS ; Skip if No Pixels to set OUT DX, AL ; Set up Screen Mask MOV ES:[DI], AH ; Write Foreground color @TGP_NO_RIGHT1BITS: DEC DI ; Adjust for Next Line Advance @TGP_NEXT_LINE: ADD DI, [BP].TGP_Width ; Point to Next Line XOR CH, CHAR_BITS ; Flip the Clip mask back DEC [BP].TGP_Lines ; Count Down Lines JZ @TGP_EXIT ; Ok... Done! JMP @TGP_DECODE_CHAR_BYTE ; Again! Hey! @TGP_EXIT: ADD SP, 08 ; Deallocate stack workspace ;POPx DI, SI, DS, BP ; Restore Saved Registers pop di pop si pop ds pop bp RET 8 ; Exit and Clean up Stack TGPRINTC ENDP ;=============================================================== ;PRINT_STR (SEG String, MaxLen%, Xpos%, Ypos%, ColorF%, ColorB%) ;=============================================================== ; ; Routine to quickly Print a null terminated ASCII string on the ; active display page up to a maximum length. ; ; ENTRY: String = Far Pointer to ASCII string to print ; MaxLen = # of characters to print if no null found ; Xpos = X position to draw Text at ; Ypos = Y position of to draw Text at ; ColorF = Color to draw text in ; ColorB = Color to set background to ; ; EXIT: No meaningful values returned ; PS_STACK STRUC DW ?x1 ; DI, SI, DS, BP DW ?x1 ; DI, SI, DS, BP DW ?x1 ; DS, DI, SI, BP DW ?x1 ; DS, DI, SI, BP DD ? ; Caller PS_ColorB DW ? ; Background Color PS_ColorF DW ? ; Text Color PS_Ypos DW ? ; Y Position to Print at PS_Xpos DW ? ; X position to Print at PS_Len DW ? ; Maximum Length of string to print PS_Text DW ?,? ; Far Ptr to Text String PS_STACK ENDS PUBLIC PRINT_STR PRINT_STR PROC FAR ;PUSHx BP, DS, SI, DI ; Preserve Important Registers push bp push ds push si push di MOV BP, SP ; Set up Stack Frame @PS_Print_It: MOV CX, [BP].PS_Len ; Get Remaining text Length JCXZ @PS_Exit ; Exit when out of text LES DI, d [BP].PS_Text ; ES:DI -> Current Char in Text MOV AL, ES:[DI] ; AL = Text Character AND AX, 00FFh ; Clear High Word JZ @PS_Exit ; Exit if null character DEC [BP].PS_Len ; Remaining Text length-- INC [BP].PS_Text ; Point to Next text char ; Set up Call to GPRINTC PUSH AX ; Set Character Parameter MOV BX, [BP].PS_Xpos ; Get Xpos PUSH BX ; Set Xpos Parameter ADD BX, 8 ; Advance 1 Char to Right MOV [BP].PS_Xpos, BX ; Save for next time through MOV BX, [BP].PS_Ypos ; Get Ypos PUSH BX ; Set Ypos Parameter MOV BX, [BP].PS_ColorF ; Get Text Color PUSH BX ; Set ColorF Parameter MOV BX, [BP].PS_ColorB ; Get Background Color PUSH BX ; Set ColorB Parameter CALL f GPRINTC ; Print Character! JMP s @PS_Print_It ; Process next character @PS_Exit: ;POPx DI, SI, DS, BP ; Restore Saved Registers pop di pop si pop ds pop bp RET 14 ; Exit and Clean up Stack PRINT_STR ENDP ;================================================================ ;TPRINT_STR (SEG String, MaxLen%, Xpos%, Ypos%, ColorF%, ColorB%) ;================================================================ ; ; Routine to quickly transparently Print a null terminated ASCII ; string on the active display page up to a maximum length. ; ; ENTRY: String = Far Pointer to ASCII string to print ; MaxLen = # of characters to print if no null found ; Xpos = X position to draw Text at ; Ypos = Y position of to draw Text at ; ColorF = Color to draw text in ; ; EXIT: No meaningful values returned ; TPS_STACK STRUC DW ?x1 ; DI, SI, DS, BP DW ?x1 ; DI, SI, DS, BP DW ?x1 ; DS, DI, SI, BP DW ?x1 ; DS, DI, SI, BP DD ? ; Caller TPS_ColorF DW ? ; Text Color TPS_Ypos DW ? ; Y Position to Print at TPS_Xpos DW ? ; X position to Print at TPS_Len DW ? ; Maximum Length of string to print TPS_Text DW ?,? ; Far Ptr to Text String TPS_STACK ENDS PUBLIC TPRINT_STR TPRINT_STR PROC FAR ;PUSHx BP, DS, SI, DI ; Preserve Important Registers push bp push ds push si push di MOV BP, SP ; Set up Stack Frame @TPS_Print_It: MOV CX, [BP].TPS_Len ; Get Remaining text Length JCXZ @TPS_Exit ; Exit when out of text LES DI, d [BP].TPS_Text ; ES:DI -> Current Char in Text MOV AL, ES:[DI] ; AL = Text Character AND AX, 00FFh ; Clear High Word JZ @TPS_Exit ; Exit if null character DEC [BP].TPS_Len ; Remaining Text length-- INC [BP].TPS_Text ; Point to Next text char ; Set up Call to TGPRINTC PUSH AX ; Set Character Parameter MOV BX, [BP].TPS_Xpos ; Get Xpos PUSH BX ; Set Xpos Parameter ADD BX, 8 ; Advance 1 Char to Right MOV [BP].TPS_Xpos, BX ; Save for next time through MOV BX, [BP].TPS_Ypos ; Get Ypos PUSH BX ; Set Ypos Parameter MOV BX, [BP].TPS_ColorF ; Get Text Color PUSH BX ; Set ColorF Parameter CALL f TGPRINTC ; Print Character! JMP s @TPS_Print_It ; Process next character @TPS_Exit: ;POPx DI, SI, DS, BP ; Restore Saved Registers pop di pop si pop ds pop bp RET 12 ; Exit and Clean up Stack TPRINT_STR ENDP ;=========================================== ;SET_DISPLAY_FONT(SEG FontData, FontNumber%) ;=========================================== ; ; Allows the user to specify their own font data for ; wither the lower or upper 128 characters. ; ; ENTRY: FontData = Far Pointer to Font Bitmaps ; FontNumber = Which half of set this is ; = 0, Lower 128 characters ; = 1, Upper 128 characters ; ; EXIT: No meaningful values returned ; SDF_STACK STRUC DW ? ; BP DD ? ; Caller SDF_Which DW ? ; Hi Table/Low Table Flag SDF_Font DD ? ; Far Ptr to Font Table SDF_STACK ENDS PUBLIC SET_DISPLAY_FONT SET_DISPLAY_FONT PROC FAR PUSH BP ; Preserve Registers MOV BP, SP ; Set up Stack Frame LES DI, [BP].SDF_Font ; Get Far Ptr to Font MOV SI, o CHARSET_LOW ; Assume Lower 128 chars TEST [BP].SDF_Which, 1 ; Font #1 selected? JZ @SDF_Set_Font ; If not, skip ahead MOV SI, o CHARSET_HI ; Ah, really it's 128-255 @SDF_Set_Font: MOV [SI], DI ; Set Font Pointer Offset MOV [SI+2], ES ; Set Font Pointer Segment POP BP ; Restore Registers RET 6 ; We are Done.. Outa here SET_DISPLAY_FONT ENDP ; ===== BITMAP (SPRITE) DISPLAY ROUTINES ===== ;====================================================== ;DRAW_BITMAP (SEG Image, Xpos%, Ypos%, Width%, Height%) ;====================================================== ; ; Draws a variable sized Graphics Bitmap such as a ; picture or an Icon on the current Display Page in ; Mode X. The Bitmap is stored in a linear byte array ; corresponding to (0,0) (1,0), (2,0) .. (Width, Height) ; This is the same linear manner as mode 13h graphics. ; ; ENTRY: Image = Far Pointer to Bitmap Data ; Xpos = X position to Place Upper Left pixel at ; Ypos = Y position to Place Upper Left pixel at ; Width = Width of the Bitmap in Pixels ; Height = Height of the Bitmap in Pixels ; ; EXIT: No meaningful values returned ; DB_STACK STRUC DB_LineO DW ? ; Offset to Next Line DB_PixCount DW ? ; (Minimum) # of Pixels/Line DB_Start DW ? ; Addr of Upper Left Pixel DB_PixSkew DW ? ; # of bytes to Adjust EOL DB_SkewFlag DW ? ; Extra Pix on Plane Flag DW ?x1 ; DI, SI, DS, BP DW ?x1 ; DI, SI, DS, BP DW ?x1 ; DS, DI, SI, BP DW ?x1 ; DS, DI, SI, BP DD ? ; Caller DB_Height DW ? ; Height of Bitmap in Pixels DB_Width DW ? ; Width of Bitmap in Pixels DB_Ypos DW ? ; Y position to Draw Bitmap at DB_Xpos DW ? ; X position to Draw Bitmap at DB_Image DD ? ; Far Pointer to Graphics Bitmap DB_STACK ENDS PUBLIC DRAW_BITMAP DRAW_BITMAP PROC FAR ;PUSHx BP, DS, SI, DI ; Preserve Important Registers push bp push ds push si push di SUB SP, 10 ; Allocate workspace MOV BP, SP ; Set up Stack Frame LES DI, d CURRENT_PAGE ; Point to Active VGA Page CLD ; Direction Flag = Forward MOV AX, [BP].DB_Ypos ; Get UL Corner Ypos MUL SCREEN_WIDTH ; AX = Offset to Line Ypos MOV BX, [BP].DB_Xpos ; Get UL Corner Xpos MOV CL, BL ; Save Plane # in CL SHR BX, 1 ; Xpos/4 = Offset Into Line SHR BX, 1 ; Xpos/4 = Offset Into Line ADD DI, AX ; ES:DI -> Start of Line ADD DI, BX ; ES:DI -> Upper Left Pixel MOV [BP].DB_Start, DI ; Save Starting Addr ; Compute line to line offset MOV BX, [BP].DB_Width ; Get Width of Image MOV DX, BX ; Save Copy in DX SHR BX, 1 ; /4 = width in bands SHR BX, 1 ; /4 = width in bands MOV AX, SCREEN_WIDTH ; Get Screen Width SUB AX, BX ; - (Bitmap Width/4) MOV [BP].DB_LineO, AX ; Save Line Width offset MOV [BP].DB_PixCount, BX ; Minimum # pix to copy AND DX, PLANE_BITS ; Get "partial band" size (0-3) MOV [BP].DB_PixSkew, DX ; Also End of Line Skew MOV [BP].DB_SkewFlag, DX ; Save as Flag/Count AND CX, PLANE_BITS ; CL = Starting Plane # MOV AX, MAP_MASK_PLANE2 ; Plane Mask & Plane Select SHL AH, CL ; Select correct Plane OUT_16 SC_Index, AX ; Select Plane... MOV BH, AH ; BH = Saved Plane Mask MOV BL, 4 ; BL = Planes to Copy @DB_COPY_PLANE: LDS SI, [BP].DB_Image ; DS:SI-> Source Image MOV DX, [BP].DB_Height ; # of Lines to Copy MOV DI, [BP].DB_Start ; ES:DI-> Dest pos @DB_COPY_LINE: MOV CX, [BP].DB_PixCount ; Min # to copy TEST CL, 0FCh ; 16+PixWide? JZ @DB_COPY_REMAINDER ; Nope... ; Pixel Copy loop has been unrolled to x4 @DB_COPY_LOOP: MOVSB ; Copy Bitmap Pixel ADD SI, 3 ; Skip to Next Byte in same plane MOVSB ; Copy Bitmap Pixel ADD SI, 3 ; Skip to Next Byte in same plane MOVSB ; Copy Bitmap Pixel ADD SI, 3 ; Skip to Next Byte in same plane MOVSB ; Copy Bitmap Pixel ADD SI, 3 ; Skip to Next Byte in same plane SUB CL, 4 ; Pixels to Copy=-4 TEST CL, 0FCh ; 4+ Pixels Left? JNZ @DB_COPY_LOOP ; if so, do another block @DB_COPY_REMAINDER: JCXZ @DB_NEXT_LINE ; Any Pixels left on line @DB_COPY2: MOVSB ; Copy Bitmap Pixel ADD SI,3 ; Skip to Next Byte in same plane LOOPx CX, @DB_COPY2 ; Pixels to Copy--, Loop until done @DB_NEXT_LINE: ; any Partial Pixels? (some planes only) OR CX, [BP].DB_SkewFlag ; Get Skew Count JZ @DB_NEXT2 ; if no partial pixels MOVSB ; Copy Bitmap Pixel DEC DI ; Back up to align DEC SI ; Back up to align @DB_NEXT2: ADD SI, [BP].DB_PixSkew ; Adjust Skew ADD DI, [BP].DB_LineO ; Set to Next Display Line LOOPx DX, @DB_COPY_LINE ; Lines to Copy--, Loop if more ; Copy Next Plane.... DEC BL ; Planes to Go-- JZ @DB_Exit ; Hey! We are done ROL BH, 1 ; Next Plane in line... OUT_8 SC_Data, BH ; Select Plane CMP AL, 12h ; Carry Set if AL=11h ADC [BP].DB_Start, 0 ; Screen Addr =+Carry INC w [BP].DB_Image ; Start @ Next Byte SUB [BP].DB_SkewFlag, 1 ; Reduce Planes to Skew ADC [BP].DB_SkewFlag, 0 ; Back to 0 if it was -1 JMP s @DB_COPY_PLANE ; Go Copy the Next Plane @DB_Exit: ADD SP, 10 ; Deallocate workspace ;POPx DI, SI, DS, BP ; Restore Saved Registers pop di pop si pop ds pop bp RET 12 ; Exit and Clean up Stack DRAW_BITMAP ENDP ;======================================================= ;TDRAW_BITMAP (SEG Image, Xpos%, Ypos%, Width%, Height%) ;======================================================= ; ; Transparently Draws a variable sized Graphics Bitmap ; such as a picture or an Icon on the current Display Page ; in Mode X. Pixels with a value of 0 are not drawn, ; leaving the previous "background" contents intact. ; ; The Bitmap format is the same as for the DRAW_BITMAP function. ; ; ENTRY: Image = Far Pointer to Bitmap Data ; Xpos = X position to Place Upper Left pixel at ; Ypos = Y position to Place Upper Left pixel at ; Width = Width of the Bitmap in Pixels ; Height = Height of the Bitmap in Pixels ; ; EXIT: No meaningful values returned ; TB_STACK STRUC TB_LineO DW ? ; Offset to Next Line TB_PixCount DW ? ; (Minimum) # of Pixels/Line TB_Start DW ? ; Addr of Upper Left Pixel TB_PixSkew DW ? ; # of bytes to Adjust EOL TB_SkewFlag DW ? ; Extra Pix on Plane Flag DW ?x1 ; DI, SI, DS, BP DW ?x1 ; DI, SI, DS, BP DW ?x1 ; DS, DI, SI, BP DW ?x1 ; DS, DI, SI, BP DD ? ; Caller TB_Height DW ? ; Height of Bitmap in Pixels TB_Width DW ? ; Width of Bitmap in Pixels TB_Ypos DW ? ; Y position to Draw Bitmap at TB_Xpos DW ? ; X position to Draw Bitmap at TB_Image DD ? ; Far Pointer to Graphics Bitmap TB_STACK ENDS PUBLIC TDRAW_BITMAP TDRAW_BITMAP PROC FAR ;PUSHx BP, DS, SI, DI ; Preserve Important Registers push bp push ds push si push di SUB SP, 10 ; Allocate workspace MOV BP, SP ; Set up Stack Frame LES DI, d CURRENT_PAGE ; Point to Active VGA Page CLD ; Direction Flag = Forward MOV AX, [BP].TB_Ypos ; Get UL Corner Ypos MUL SCREEN_WIDTH ; AX = Offset to Line Ypos MOV BX, [BP].TB_Xpos ; Get UL Corner Xpos MOV CL, BL ; Save Plane # in CL SHR BX, 1 ; Xpos/4 = Offset Into Line SHR BX, 1 ; Xpos/4 = Offset Into Line ADD DI, AX ; ES:DI -> Start of Line ADD DI, BX ; ES:DI -> Upper Left Pixel MOV [BP].TB_Start, DI ; Save Starting Addr ; Compute line to line offset MOV BX, [BP].TB_Width ; Get Width of Image MOV DX, BX ; Save Copy in DX SHR BX, 1 ; /4 = width in bands SHR BX, 1 ; /4 = width in bands MOV AX, SCREEN_WIDTH ; Get Screen Width SUB AX, BX ; - (Bitmap Width/4) MOV [BP].TB_LineO, AX ; Save Line Width offset MOV [BP].TB_PixCount, BX ; Minimum # pix to copy AND DX, PLANE_BITS ; Get "partial band" size (0-3) MOV [BP].TB_PixSkew, DX ; Also End of Line Skew MOV [BP].TB_SkewFlag, DX ; Save as Flag/Count AND CX, PLANE_BITS ; CL = Starting Plane # MOV AX, MAP_MASK_PLANE2 ; Plane Mask & Plane Select SHL AH, CL ; Select correct Plane OUT_16 SC_Index, AX ; Select Plane... MOV BH, AH ; BH = Saved Plane Mask MOV BL, 4 ; BL = Planes to Copy @TB_COPY_PLANE: LDS SI, [BP].TB_Image ; DS:SI-> Source Image MOV DX, [BP].TB_Height ; # of Lines to Copy MOV DI, [BP].TB_Start ; ES:DI-> Dest pos ; Here AH is set with the value to be considered ; "Transparent". It can be changed! MOV AH, 0 ; Value to Detect 0 @TB_COPY_LINE: MOV CX, [BP].TB_PixCount ; Min # to copy TEST CL, 0FCh ; 16+PixWide? JZ @TB_COPY_REMAINDER ; Nope... ; Pixel Copy loop has been unrolled to x4 @TB_COPY_LOOP: LODSB ; Get Pixel Value in AL ADD SI, 3 ; Skip to Next Byte in same plane CMP AL, AH ; It is "Transparent"? JE @TB_SKIP_01 ; Skip ahead if so MOV ES:[DI], AL ; Copy Pixel to VGA screen @TB_SKIP_01: LODSB ; Get Pixel Value in AL ADD SI, 3 ; Skip to Next Byte in same plane CMP AL, AH ; It is "Transparent"? JE @TB_SKIP_02 ; Skip ahead if so MOV ES:[DI+1], AL ; Copy Pixel to VGA screen @TB_SKIP_02: LODSB ; Get Pixel Value in AL ADD SI, 3 ; Skip to Next Byte in same plane CMP AL, AH ; It is "Transparent"? JE @TB_SKIP_03 ; Skip ahead if so MOV ES:[DI+2], AL ; Copy Pixel to VGA screen @TB_SKIP_03: LODSB ; Get Pixel Value in AL ADD SI, 3 ; Skip to Next Byte in same plane CMP AL, AH ; It is "Transparent"? JE @TB_SKIP_04 ; Skip ahead if so MOV ES:[DI+3], AL ; Copy Pixel to VGA screen @TB_SKIP_04: ADD DI, 4 ; Adjust Pixel Write Location SUB CL, 4 ; Pixels to Copy=-4 TEST CL, 0FCh ; 4+ Pixels Left? JNZ @TB_COPY_LOOP ; if so, do another block @TB_COPY_REMAINDER: JCXZ @TB_NEXT_LINE ; Any Pixels left on line @TB_COPY2: LODSB ; Get Pixel Value in AL ADD SI, 3 ; Skip to Next Byte in same plane CMP AL, AH ; It is "Transparent"? JE @TB_SKIP_05 ; Skip ahead if so MOV ES:[DI], AL ; Copy Pixel to VGA screen @TB_SKIP_05: INC DI ; Advance Dest Addr LOOPx CX, @TB_COPY2 ; Pixels to Copy--, Loop until done @TB_NEXT_LINE: ; any Partial Pixels? (some planes only) OR CX, [BP].TB_SkewFlag ; Get Skew Count JZ @TB_NEXT2 ; if no partial pixels LODSB ; Get Pixel Value in AL DEC SI ; Backup to Align CMP AL, AH ; It is "Transparent"? JE @TB_NEXT2 ; Skip ahead if so MOV ES:[DI], AL ; Copy Pixel to VGA screen @TB_NEXT2: ADD SI, [BP].TB_PixSkew ; Adjust Skew ADD DI, [BP].TB_LineO ; Set to Next Display Line LOOPx DX, @TB_COPY_LINE ; Lines to Copy--, Loop if More ;Copy Next Plane.... DEC BL ; Planes to Go-- JZ @TB_Exit ; Hey! We are done ROL BH, 1 ; Next Plane in line... OUT_8 SC_Data, BH ; Select Plane CMP AL, 12h ; Carry Set if AL=11h ADC [BP].TB_Start, 0 ; Screen Addr =+Carry INC w [BP].TB_Image ; Start @ Next Byte SUB [BP].TB_SkewFlag, 1 ; Reduce Planes to Skew ADC [BP].TB_SkewFlag, 0 ; Back to 0 if it was -1 JMP @TB_COPY_PLANE ; Go Copy the next Plane @TB_Exit: ADD SP, 10 ; Deallocate workspace ;POPx DI, SI, DS, BP ; Restore Saved Registers pop di pop si pop ds pop bp RET 12 ; Exit and Clean up Stack TDRAW_BITMAP ENDP ; ==== VIDEO MEMORY to VIDEO MEMORY COPY ROUTINES ===== ;================================== ;COPY_PAGE (SourcePage%, DestPage%) ;================================== ; ; Duplicate on display page onto another ; ; ENTRY: SourcePage = Display Page # to Duplicate ; DestPage = Display Page # to hold copy ; ; EXIT: No meaningful values returned ; CP_STACK STRUC DW ?x1 ; DI, SI, DS, BP DW ?x1 ; DI, SI, DS, BP DW ?x1 ; DS, DI, SI, BP DW ?x1 ; DS, DI, SI, BP DD ? ; Caller CP_DestP DW ? ; Page to hold copied image CP_SourceP DW ? ; Page to Make copy from CP_STACK ENDS PUBLIC COPY_PAGE COPY_PAGE PROC FAR ;PUSHx BP, DS, SI, DI ; Preserve Important Registers push bp push ds push si push di MOV BP, SP ; Set up Stack Frame CLD ; Block Xfer Forwards ; Make sure Page #'s are valid MOV AX, [BP].CP_SourceP ; Get Source Page # CMP AX, LAST_PAGE ; is it > Max Page #? JAE @CP_Exit ; if so, abort MOV BX, [BP].CP_DestP ; Get Destination Page # CMP BX, LAST_PAGE ; is it > Max Page #? JAE @CP_Exit ; if so, abort CMP AX, BX ; Pages #'s the same? JE @CP_Exit ; if so, abort ; Setup DS:SI and ES:DI to Video Pages SHL BX, 1 ; Scale index to Word MOV DI, PAGE_ADDR[BX] ; Offset to Dest Page MOV BX, AX ; Index to Source page SHL BX, 1 ; Scale index to Word MOV SI, PAGE_ADDR[BX] ; Offset to Source Page MOV CX, PAGE_SIZE ; Get size of Page MOV AX, CURRENT_SEGMENT ; Get Video Mem Segment MOV ES, AX ; ES:DI -> Dest Page MOV DS, AX ; DS:SI -> Source Page ; Setup VGA registers for Mem to Mem copy OUT_16 GC_Index, LATCHES_ON ; Data from Latches = on OUT_16 SC_Index, ALL_PLANES_ON ; Copy all Planes ; Note.. Do *NOT* use MOVSW or MOVSD - they will ; Screw with the latches which are 8 bits x 4 REP MOVSB ; Copy entire Page! ; Reset VGA for normal memory access OUT_16 GC_Index, LATCHES_OFF ; Data from Latches = off @CP_Exit: ;POPx DI, SI, DS, BP ; Restore Saved Registers pop di pop si pop ds pop bp RET 4 ; Exit and Clean up Stack COPY_PAGE ENDP ;========================================================================== ;COPY_BITMAP (SourcePage%, X1%, Y1%, X2%, Y2%, DestPage%, DestX1%, DestY1%) ;========================================================================== ; ; Copies a Bitmap Image from one Display Page to Another ; This Routine is Limited to copying Images with the same ; Plane Alignment. To Work: (X1 MOD 4) must = (DestX1 MOD 4) ; Copying an Image to the Same Page is supported, but results ; may be defined when the when the rectangular areas ; (X1, Y1) - (X2, Y2) and (DestX1, DestY1) - ; (DestX1+(X2-X1), DestY1+(Y2-Y1)) overlap... ; No Paramter checking to done to insure that ; X2 >= X1 and Y2 >= Y1. Be Careful... ; ; ENTRY: SourcePage = Display Page # with Source Image ; X1 = Upper Left Xpos of Source Image ; Y1 = Upper Left Ypos of Source Image ; X2 = Lower Right Xpos of Source Image ; Y2 = Lower Right Ypos of Source Image ; DestPage = Display Page # to copy Image to ; DestX1 = Xpos to Copy UL Corner of Image to ; DestY1 = Ypos to Copy UL Corner of Image to ; ; EXIT: AX = Success Flag: 0 = Failure / -1= Success ; CB_STACK STRUC CB_Height DW ? ; Height of Image in Lines CB_Width DW ? ; Width of Image in "bands" DW ?x1 ; DI, SI, DS, BP DW ?x1 ; DI, SI, DS, BP DW ?x1 ; DS, DI, SI, BP DW ?x1 ; DS, DI, SI, BP DD ? ; Caller CB_DestY1 DW ? ; Destination Ypos CB_DestX1 DW ? ; Destination Xpos CB_DestP DW ? ; Page to Copy Bitmap To CB_Y2 DW ? ; LR Ypos of Image CB_X2 DW ? ; LR Xpos of Image CB_Y1 DW ? ; UL Ypos of Image CB_X1 DW ? ; UL Xpos of Image CB_SourceP DW ? ; Page containing Source Bitmap CB_STACK ENDS PUBLIC COPY_BITMAP COPY_BITMAP PROC FAR ;PUSHx BP, DS, SI, DI ; Preserve Important Registers push bp push ds push si push di SUB SP, 4 ; Allocate WorkSpace on Stack MOV BP, SP ; Set up Stack Frame ; Prep Registers (and keep jumps short!) MOV ES, CURRENT_SEGMENT ; ES -> VGA Ram CLD ; Block Xfer Forwards ; Make sure Parameters are valid MOV BX, [BP].CB_SourceP ; Get Source Page # CMP BX, LAST_PAGE ; is it > Max Page #? JAE @CB_Abort ; if so, abort MOV CX, [BP].CB_DestP ; Get Destination Page # CMP CX, LAST_PAGE ; is it > Max Page #? JAE @CB_Abort ; if so, abort MOV AX, [BP].CB_X1 ; Get Source X1 XOR AX, [BP].CB_DestX1 ; Compare Bits 0-1 AND AX, PLANE_BITS ; Check Plane Bits JNZ @CB_Abort ; They should cancel out ; Setup for Copy processing OUT_8 SC_INDEX, MAP_MASK ; Set up for Plane Select OUT_16 GC_Index, LATCHES_ON ; Data from Latches = on ; Compute Info About Images, Setup ES:SI & ES:DI MOV AX, [BP].CB_Y2 ; Height of Bitmap in lines SUB AX, [BP].CB_Y1 ; is Y2 - Y1 + 1 INC AX ; (add 1 since were not 0 based) MOV [BP].CB_Height, AX ; Save on Stack for later use MOV AX, [BP].CB_X2 ; Get # of "Bands" of 4 Pixels MOV DX, [BP].CB_X1 ; the Bitmap Occupies as X2-X1 SHR AX, 1 ; Get X2 Band (X2 / 4) SHR DX, 1 ; Get X1 Band (X1 / 4) SHR AX, 1 ; Get X2 Band (X2 / 4) SHR DX, 1 ; Get X1 Band (X1 / 4) SUB AX, DX ; AX = # of Bands - 1 INC AX ; AX = # of Bands MOV [BP].CB_Width, AX ; Save on Stack for later use SHL BX, 1 ; Scale Source Page to Word MOV SI, PAGE_ADDR[BX] ; SI = Offset of Source Page MOV AX, [BP].CB_Y1 ; Get Source Y1 Line MUL SCREEN_WIDTH ; AX = Offset to Line Y1 ADD SI, AX ; SI = Offset to Line Y1 MOV AX, [BP].CB_X1 ; Get Source X1 SHR AX, 1 ; X1 / 4 = Byte offset SHR AX, 1 ; X1 / 4 = Byte offset ADD SI, AX ; SI = Byte Offset to (X1,Y1) MOV BX, CX ; Dest Page Index to BX SHL BX, 1 ; Scale Source Page to Word MOV DI, PAGE_ADDR[BX] ; DI = Offset of Dest Page MOV AX, [BP].CB_DestY1 ; Get Dest Y1 Line MUL SCREEN_WIDTH ; AX = Offset to Line Y1 ADD DI, AX ; DI = Offset to Line Y1 MOV AX, [BP].CB_DestX1 ; Get Dest X1 SHR AX, 1 ; X1 / 4 = Byte offset SHR AX, 1 ; X1 / 4 = Byte offset ADD DI, AX ; DI = Byte Offset to (D-X1,D-Y1) MOV CX, [BP].CB_Width ; CX = Width of Image (Bands) DEC CX ; CX = 1? JE @CB_Only_One_Band ; 0 Means Image Width of 1 Band MOV BX, [BP].CB_X1 ; Get Source X1 AND BX, PLANE_BITS ; Aligned? (bits 0-1 = 00?) JZ @CB_Check_Right ; if so, check right alignment JNZ @CB_Left_Band ; not aligned? well.. @CB_Abort: mov ax,0 ; Return False (Failure) JMP @CB_Exit ; and Finish Up ; Copy when Left & Right Clip Masks overlap... @CB_Only_One_Band: MOV BX, [BP].CB_X1 ; Get Left Clip Mask AND BX, PLANE_BITS ; Mask out Row # MOV AL, Left_Clip_Mask[BX] ; Get Left Edge Mask MOV BX, [BP].CB_X2 ; Get Right Clip Mask AND BX, PLANE_BITS ; Mask out Row # AND AL, Right_Clip_Mask[BX] ; Get Right Edge Mask byte OUT_8 SC_Data, AL ; Clip For Left & Right Masks MOV CX, [BP].CB_Height ; CX = # of Lines to Copy MOV DX, SCREEN_WIDTH ; DX = Width of Screen mov bx,0 ; BX = Offset into Image @CB_One_Loop: MOV AL, ES:[SI+BX] ; Load Latches MOV ES:[DI+BX], AL ; Unload Latches ADD BX, DX ; Advance Offset to Next Line LOOPjz CX, @CB_One_Done ; Exit Loop if Finished MOV AL, ES:[SI+BX] ; Load Latches MOV ES:[DI+BX], AL ; Unload Latches ADD BX, DX ; Advance Offset to Next Line LOOPx CX, @CB_One_Loop ; Loop until Finished @CB_One_Done: JMP @CB_Finish ; Outa Here! ; Copy Left Edge of Bitmap @CB_Left_Band: OUT_8 SC_Data, Left_Clip_Mask[BX] ; Set Left Edge Plane Mask MOV CX, [BP].CB_Height ; CX = # of Lines to Copy MOV DX, SCREEN_WIDTH ; DX = Width of Screen mov bx,0 ; BX = Offset into Image @CB_Left_Loop: MOV AL, ES:[SI+BX] ; Load Latches MOV ES:[DI+BX], AL ; Unload Latches ADD BX, DX ; Advance Offset to Next Line LOOPjz CX, @CB_Left_Done ; Exit Loop if Finished MOV AL, ES:[SI+BX] ; Load Latches MOV ES:[DI+BX], AL ; Unload Latches ADD BX, DX ; Advance Offset to Next Line LOOPx CX, @CB_Left_Loop ; Loop until Finished @CB_Left_Done: INC DI ; Move Dest Over 1 band INC SI ; Move Source Over 1 band DEC [BP].CB_Width ; Band Width-- ; Determine if Right Edge of Bitmap needs special copy @CB_Check_Right: MOV BX, [BP].CB_X2 ; Get Source X2 AND BX, PLANE_BITS ; Aligned? (bits 0-1 = 11?) CMP BL, 03h ; Plane = 3? JE @CB_Copy_Middle ; Copy the Middle then! ; Copy Right Edge of Bitmap @CB_Right_Band: OUT_8 SC_Data, Right_Clip_Mask[BX] ; Set Right Edge Plane Mask DEC [BP].CB_Width ; Band Width-- MOV CX, [BP].CB_Height ; CX = # of Lines to Copy MOV DX, SCREEN_WIDTH ; DX = Width of Screen MOV BX, [BP].CB_Width ; BX = Offset to Right Edge @CB_Right_Loop: MOV AL, ES:[SI+BX] ; Load Latches MOV ES:[DI+BX], AL ; Unload Latches ADD BX, DX ; Advance Offset to Next Line LOOPjz CX, @CB_Right_Done ; Exit Loop if Finished MOV AL, ES:[SI+BX] ; Load Latches MOV ES:[DI+BX], AL ; Unload Latches ADD BX, DX ; Advance Offset to Next Line LOOPx CX, @CB_Right_Loop ; Loop until Finished @CB_Right_Done: ; Copy the Main Block of the Bitmap @CB_Copy_Middle: MOV CX, [BP].CB_Width ; Get Width Remaining JCXZ @CB_Finish ; Exit if Done OUT_8 SC_Data, ALL_PLANES ; Copy all Planes MOV DX, SCREEN_WIDTH ; Get Width of Screen minus SUB DX, CX ; Image width (for Adjustment) MOV AX, [BP].CB_Height ; AX = # of Lines to Copy MOV BX, CX ; BX = Quick REP reload count MOV CX, ES ; Move VGA Segment MOV DS, CX ; Into DS ; Actual Copy Loop. REP MOVSB does the work @CB_Middle_Copy: MOV CX, BX ; Recharge Rep Count REP MOVSB ; Move Bands LOOPjz AX, @CB_Finish ; Exit Loop if Finished ADD SI, DX ; Adjust DS:SI to Next Line ADD DI, DX ; Adjust ES:DI to Next Line MOV CX, BX ; Recharge Rep Count REP MOVSB ; Move Bands ADD SI, DX ; Adjust DS:SI to Next Line ADD DI, DX ; Adjust ES:DI to Next Line LOOPx AX, @CB_Middle_Copy ; Copy Lines until Done @CB_Finish: OUT_16 GC_Index, LATCHES_OFF ; Data from Latches = on @CB_Exit: ADD SP, 04 ; Deallocate stack workspace ;POPx DI, SI, DS, BP ; Restore Saved Registers pop di pop si pop ds pop bp RET 16 ; Exit and Clean up Stack COPY_BITMAP ENDP END ; End of Code Segment