home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C!T ROM 2
/
ctrom_ii_b.zip
/
ctrom_ii_b
/
PROGRAM
/
ASSEMBLE
/
MODEX104
/
MODEX.ASM
< prev
next >
Wrap
Assembly Source File
|
1993-05-14
|
117KB
|
3,296 lines
;========================================================
; 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 Medium
.286
; ===== MACROS =====
; Macro to OUT a 16 bit value to an I/O port
OUT_16 MACRO Register, Value
IFDIFI <Register>, <DX> ; If DX not setup
MOV DX, Register ; then Select Register
ENDIF
IFDIFI <Value>, <AX> ; 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 <Register>, <DX> ; If DX not setup
MOV DX, Register ; then Select Register
ENDIF
IFDIFI <Value>, <AL> ; 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 <R1>
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 <R1>
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 <Register>
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
?x4 EQU <?,?,?,?>
?x3 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 / -1= Success
;
SVM_STACK STRUC
SVM_Table DW ? ; Offset of Mode Info Table
DW ?x4 ; 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
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_BadModeSetup ; 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!
CMP CL, CS:[SI].M_Pages ; Check # Pages for mode
JA @SVM_BadModeSetup ; Report Error if too Many Pages
JCXZ @SVM_BadModeSetup ; 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_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 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...
@SVM_BadModeSetup:
CLR 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
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, 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 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
CLR BX ; 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
CLR AX ; 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
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
MOV BP, SP ; Set up Stack Frame
CLR AX ; 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
CLR AH ; Hi Byte = 0
PUSH AX ; Push # Pages
CALL f SET_VGA_MODEX ; Set up Mode X!
@SMX_Exit:
POPx SI, BP ; Restore Registers
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
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
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
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, 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
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
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
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 #
OUT_16 GC_INDEX, AX ; Select Plane to read in
CLR AH ; Clear Return Value Hi byte
MOV AL, ES:[DI+BX] ; Get Color of Pixel
POPx DI, BP ; Restore Saved Registers
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 ?x4 ; 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
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_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
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 ?x3 ; 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
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, 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 ; 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, 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 #
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, 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 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:
CLR AX ; 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:
CLR 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 = 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:
CLR AX ; 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:
CLR 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 = 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
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
CLR 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
;===========================================================
;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 ?x3 ; 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
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
CLR AX, 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
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 ?x3 ; 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
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
CLR AX, 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
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, 2 ; 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 ?x4 ; DI, SI, DS, 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
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, 2 ; 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, 3 ; * 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...
CLR AX ; Clear AX
CLR BL ; 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 @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, 4 ; 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, 4 ; 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
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 ?x4 ; DI, SI, DS, 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
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, 2 ; 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, 3 ; * 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
CLR BL ; 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, 4 ; 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, 4 ; 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
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 ?x4 ; DI, SI, DS, 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
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
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 ?x4 ; DI, SI, DS, 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
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
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 ?x4 ; DI, SI, DS, 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
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, 2 ; 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, 2 ; /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
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 ?x4 ; DI, SI, DS, 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
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, 2 ; 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, 2 ; /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
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 ?x4 ; DI, SI, DS, 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
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
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 ?x4 ; DI, SI, DS, 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
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, 2 ; Get X2 Band (X2 / 4)
SHR DX, 2 ; 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, 2 ; 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, 2 ; 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:
CLR AX ; 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
CLR BX ; 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
CLR BX ; 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
RET 16 ; Exit and Clean up Stack
COPY_BITMAP ENDP
END ; End of Code Segment