]> 4ch.mooo.com Git - 16.git/blob - src/lib/modex/modex.asm
16_ca needs huge amounts of work and I should remember what needs to be done soon...
[16.git] / src / lib / modex / modex.asm
1 ;========================================================\r
2 ; MODEX.ASM - A Complete Mode X Library\r
3 ;\r
4 ; Version 1.04 Release, 3 May 1993, By Matt Pritchard\r
5 ; With considerable input from Michael Abrash\r
6 ;\r
7 ; The following information is donated to the public domain in\r
8 ; the hopes that save other programmers much frustration.\r
9 ;\r
10 ; If you do use this code in a product, it would be nice if\r
11 ; you include a line like "Mode X routines by Matt Pritchard"\r
12 ; in the credits.\r
13 ;\r
14 ; =========================================================\r
15 ;\r
16 ; All of this code is designed to be assembled with MASM 5.10a\r
17 ; but TASM 3.0 could be used as well.\r
18 ;\r
19 ; The routines contained are designed for use in a MEDIUM model\r
20 ; program.  All Routines are FAR, and is assumed that a DGROUP\r
21 ; data segment exists and that DS will point to it on entry.\r
22 ;\r
23 ; For all routines, the AX, BX, CX, DX, ES and FLAGS registers\r
24 ; will not be preserved, while the DS, BP, SI and DI registers\r
25 ; will be preserved.\r
26 ;\r
27 ; Unless specifically noted, All Parameters are assumed to be\r
28 ; "PASSED BY VALUE".  That is, the actual value is placed on\r
29 ; the stack.  When a reference is passed it is assumed to be\r
30 ; a near pointer to a variable in the DGROUP segment.\r
31 ;\r
32 ; Routines that return a single 16-Bit integer value will\r
33 ; return that value in the AX register.\r
34 ;\r
35 ; This code will *NOT* run on an 8086/8088 because 80286+\r
36 ; specific instructions are used.   If you have an 8088/86\r
37 ; and VGA, you can buy an 80386-40 motherboard for about\r
38 ; $160 and move into the 90's.\r
39 ;\r
40 ; This code is reasonably optimized: Most drawing loops have\r
41 ; been unrolled once and memory references are minimized by\r
42 ; keeping stuff in registers when possible.\r
43 ;\r
44 ; Error Trapping varies by Routine.  No Clipping is performed\r
45 ; so the caller should verify that all coordinates are valid.\r
46 ;\r
47 ; Several Macros are used to simplify common 2 or 3 instruction\r
48 ; sequences.  Several Single letter Text Constants also\r
49 ; simplify common assembler expressions like "WORD PTR".\r
50 ;\r
51 ; ------------------ Mode X Variations ------------------\r
52 ;\r
53 ;  Mode #  Screen Size    Max Pages   Aspect Ratio (X:Y)\r
54 ;\r
55 ;    0      320 x 200      4 Pages         1.2:1\r
56 ;    1      320 x 400      2 Pages         2.4:1\r
57 ;    2      360 x 200      3 Pages        1.35:1\r
58 ;    3      360 x 400      1 Page          2.7:1\r
59 ;    4      320 x 240      3 Pages           1:1\r
60 ;    5      320 x 480      1 Page            2:1\r
61 ;    6      360 x 240      3 Pages       1.125:1\r
62 ;    7      360 x 480      1 Page         2.25:1\r
63 ;\r
64 ; -------------------- The Legal Stuff ------------------\r
65 ;\r
66 ; No warranty, either written or implied, is made as to\r
67 ; the accuracy and usability of this code product.  Use\r
68 ; at your own risk.  Batteries not included.  Pepperoni\r
69 ; and extra cheese available for an additional charge.\r
70 ;\r
71 ; ----------------------- The Author --------------------\r
72 ;\r
73 ; Matt Pritchard is a paid programmer who'd rather be\r
74 ; writing games.  He can be reached at: P.O. Box 140264,\r
75 ; Irving, TX  75014  USA.  Michael Abrash is a living\r
76 ; god, who now works for Bill Gates (Microsoft).\r
77 ;\r
78 ; -------------------- Revision History -----------------\r
79 ; 4-12-93: v1.02 - SET_POINT & READ_POINT now saves DI\r
80 ;          SET_MODEX now saves SI\r
81 ; 5-3-93:  v1.04 - added LOAD_DAC_REGISTERS and\r
82 ;          READ_DAC_REGISTERS.  Expanded CLR Macro\r
83 ;          to handle multiple registers\r
84 ;\r
85 \r
86     PAGE    255, 132\r
87 \r
88     .MODEL Huge\r
89     ;.286\r
90 \r
91     ; ===== MACROS =====\r
92 \r
93     ; Macro to OUT a 16 bit value to an I/O port\r
94 \r
95 OUT_16 MACRO Register, Value\r
96     IFDIFI <Register>, <DX>         ; If DX not setup\r
97         MOV     DX, Register        ; then Select Register\r
98     ENDIF\r
99     IFDIFI <Value>, <AX>            ; If AX not setup\r
100         MOV     AX, Value           ; then Get Data Value\r
101     ENDIF\r
102         OUT     DX, AX              ; Set I/O Register(s)\r
103 ENDM\r
104 \r
105     ; Macro to OUT a 8 bit value to an I/O Port\r
106 \r
107 OUT_8 MACRO Register, Value\r
108     IFDIFI <Register>, <DX>         ; If DX not setup\r
109         MOV     DX, Register        ; then Select Register\r
110     ENDIF\r
111     IFDIFI <Value>, <AL>            ; If AL not Setup\r
112         MOV     AL, Value           ; then Get Data Value\r
113     ENDIF\r
114         OUT     DX, AL              ; Set I/O Register\r
115 ENDM\r
116 \r
117     ; macros to PUSH and POP multiple registers\r
118 \r
119 ; PUSHx MACRO R1, R2, R3, R4, R5, R6, R7, R8\r
120 ;     IFNB <R1>\r
121 ;         PUSH    R1              ; Save R1\r
122 ;         PUSHx   R2, R3, R4, R5, R6, R7, R8\r
123 ;     ENDIF\r
124 ; ENDM\r
125 ;\r
126 ; POPx MACRO R1, R2, R3, R4, R5, R6, R7, R8\r
127 ;     IFNB <R1>\r
128 ;         POP     R1              ; Restore R1\r
129 ;         POPx    R2, R3, R4, R5, R6, R7, R8\r
130 ;     ENDIF\r
131 ; ENDM\r
132 \r
133     ; Macro to Clear Registers to 0\r
134 \r
135 ; CLR MACRO Register, R2, R3, R4;, R5, R6\r
136 ;     IFNB <Register>\r
137 ;         XOR     Register, Register      ; Set Register = 0\r
138 ;         CLR R2, R3, R4;, R5, R6\r
139 ;     ENDIF\r
140 ; ENDM\r
141 \r
142     ; Macros to Decrement Counter & Jump on Condition\r
143 \r
144 LOOPx MACRO Register, Destination\r
145     DEC     Register                ; Counter--\r
146     JNZ     Destination             ; Jump if not 0\r
147 ENDM\r
148 \r
149 LOOPjz MACRO Register, Destination\r
150     DEC     Register                ; Counter--\r
151     JZ      Destination             ; Jump if 0\r
152 ENDM\r
153 \r
154 \r
155     ; ===== General Constants =====\r
156 \r
157     False   EQU 0\r
158     True    EQU -1\r
159     nil     EQU 0\r
160 \r
161     b       EQU BYTE PTR\r
162     w       EQU WORD PTR\r
163     d       EQU DWORD PTR\r
164     o       EQU OFFSET\r
165     f       EQU FAR PTR\r
166     s       EQU SHORT\r
167     ?x1     EQU <?>\r
168 \r
169     ; ===== VGA Register Values =====\r
170 \r
171     VGA_Segment     EQU 0A000h  ; Vga Memory Segment\r
172 \r
173     ATTRIB_Ctrl     EQU 03C0h   ; VGA Attribute Controller\r
174     GC_Index        EQU 03CEh   ; VGA Graphics Controller\r
175     SC_Index        EQU 03C4h   ; VGA Sequencer Controller\r
176     SC_Data         EQU 03C5h   ; VGA Sequencer Data Port\r
177     CRTC_Index      EQU 03D4h   ; VGA CRT Controller\r
178     CRTC_Data       EQU 03D5h   ; VGA CRT Controller Data\r
179     MISC_OUTPUT     EQU 03C2h   ; VGA Misc Register\r
180     INPUT_1         EQU 03DAh   ; Input Status #1 Register\r
181 \r
182     DAC_WRITE_ADDR  EQU 03C8h   ; VGA DAC Write Addr Register\r
183     DAC_READ_ADDR   EQU 03C7h   ; VGA DAC Read Addr Register\r
184     PEL_DATA_REG    EQU 03C9h   ; VGA DAC/PEL data Register R/W\r
185 \r
186     PIXEL_PAN_REG   EQU 033h    ; Attrib Index: Pixel Pan Reg\r
187     MAP_MASK        EQU 002h    ; Sequ Index: Write Map Mask reg\r
188     READ_MAP        EQU 004h    ; GC Index: Read Map Register\r
189     START_DISP_HI   EQU 00Ch    ; CRTC Index: Display Start Hi\r
190     START_DISP_LO   EQU 00Dh    ; CRTC Index: Display Start Lo\r
191 \r
192     MAP_MASK_PLANE1 EQU 00102h  ; Map Register + Plane 1\r
193     MAP_MASK_PLANE2 EQU 01102h  ; Map Register + Plane 1\r
194     ALL_PLANES_ON   EQU 00F02h  ; Map Register + All Bit Planes\r
195 \r
196     CHAIN4_OFF      EQU 00604h  ; Chain 4 mode Off\r
197     ASYNC_RESET     EQU 00100h  ; (A)synchronous Reset\r
198     SEQU_RESTART    EQU 00300h  ; Sequencer Restart\r
199 \r
200     LATCHES_ON      EQU 00008h  ; Bit Mask + Data from Latches\r
201     LATCHES_OFF     EQU 0FF08h  ; Bit Mask + Data from CPU\r
202 \r
203     VERT_RETRACE    EQU 08h     ; INPUT_1: Vertical Retrace Bit\r
204     PLANE_BITS      EQU 03h     ; Bits 0-1 of Xpos = Plane #\r
205     ALL_PLANES      EQU 0Fh     ; All Bit Planes Selected\r
206     CHAR_BITS       EQU 0Fh     ; Bits 0-3 of Character Data\r
207 \r
208     GET_CHAR_PTR    EQU 01130h  ; VGA BIOS Func: Get Char Set\r
209     ROM_8x8_Lo      EQU 03h     ; ROM 8x8 Char Set Lo Pointer\r
210     ROM_8x8_Hi      EQU 04h     ; ROM 8x8 Char Set Hi Pointer\r
211 \r
212     ; Constants Specific for these routines\r
213 \r
214     NUM_MODES       EQU 8       ; # of Mode X Variations\r
215 \r
216     ; Specific Mode Data Table format...\r
217 \r
218 Mode_Data_Table STRUC\r
219     M_MiscR         DB  ?       ; Value of MISC_OUTPUT register\r
220     M_Pages         DB  ?       ; Maximum Possible # of pages\r
221     M_XSize         DW  ?       ; X Size Displayed on screen\r
222     M_YSize         DW  ?       ; Y Size Displayed on screen\r
223     M_XMax          DW  ?       ; Maximum Possible X Size\r
224     M_YMax          DW  ?       ; Maximum Possible Y Size\r
225     M_CRTC          DW  ?       ; Table of CRTC register values\r
226 Mode_Data_Table ENDS\r
227 \r
228     ; ===== DGROUP STORAGE NEEDED (42 BYTES) =====\r
229 \r
230     .DATA?\r
231 \r
232 SCREEN_WIDTH    DW  0       ; Width of a line in Bytes\r
233 SCREEN_HEIGHT   DW  0       ; Vertical Height in Pixels\r
234 \r
235 LAST_PAGE       DW  0       ; # of Display Pages\r
236 PAGE_ADDR       DW  4 DUP (0)   ; Offsets to start of each page\r
237 \r
238 PAGE_SIZE       DW  0       ; Size of Page in Addr Bytes\r
239 \r
240 DISPLAY_PAGE    DW  0       ; Page # currently displayed\r
241 ACTIVE_PAGE     DW  0       ; Page # currently active\r
242 \r
243 CURRENT_PAGE    DW  0       ; Offset of current Page\r
244 CURRENT_SEGMENT DW  0       ; Segment of VGA memory\r
245 \r
246 CURRENT_XOFFSET DW  0       ; Current Display X Offset\r
247 CURRENT_YOFFSET DW  0       ; Current Display Y Offset\r
248 \r
249 CURRENT_MOFFSET DW  0       ; Current Start Offset\r
250 \r
251 MAX_XOFFSET     DW  0       ; Current Display X Offset\r
252 MAX_YOFFSET     DW  0       ; Current Display Y Offset\r
253 \r
254 CHARSET_LOW     DW  0, 0    ; Far Ptr to Char Set: 0-127\r
255 CHARSET_HI      DW  0, 0    ; Far Ptr to Char Set: 128-255\r
256 \r
257     .CODE\r
258 \r
259     ; ===== DATA TABLES =====\r
260 \r
261     ; Data Tables, Put in Code Segment for Easy Access\r
262     ; (Like when all the other Segment Registers are in\r
263     ; use!!) and reduced DGROUP requirements...\r
264 \r
265     ; Bit Mask Tables for Left/Right/Character Masks\r
266 \r
267 Left_Clip_Mask      DB  0FH, 0EH, 0CH, 08H\r
268 \r
269 Right_Clip_Mask     DB  01H, 03H, 07H, 0FH\r
270 \r
271     ; Bit Patterns for converting character fonts\r
272 \r
273 Char_Plane_Data     DB  00H,08H,04H,0CH,02H,0AH,06H,0EH\r
274                     DB  01H,09H,05H,0DH,03H,0BH,07H,0FH\r
275 \r
276         ; CRTC Register Values for Various Configurations\r
277 \r
278 MODE_Single_Line:       ; CRTC Setup Data for 400/480 Line modes\r
279         DW  04009H      ; Cell Height (1 Scan Line)\r
280         DW  00014H      ; Dword Mode off\r
281         DW  0E317H      ; turn on Byte Mode\r
282         DW  nil         ; End of CRTC Data for 400/480 Line Mode\r
283 \r
284 MODE_Double_Line:       ; CRTC Setup Data for 200/240 Line modes\r
285         DW  04109H      ; Cell Height (2 Scan Lines)\r
286         DW  00014H      ; Dword Mode off\r
287         DW  0E317H      ; turn on Byte Mode\r
288         DW  nil         ; End of CRTC Data for 200/240 Line Mode\r
289 \r
290 MODE_320_Wide:          ; CRTC Setup Data for 320 Horz Pixels\r
291         DW  05F00H      ; Horz total\r
292         DW  04F01H      ; Horz Displayed\r
293         DW  05002H      ; Start Horz Blanking\r
294         DW  08203H      ; End Horz Blanking\r
295         DW  05404H      ; Start H Sync\r
296         DW  08005H      ; End H Sync\r
297         DW  nil         ; End of CRTC Data for 320 Horz pixels\r
298 \r
299 MODE_360_Wide:          ; CRTC Setup Data for 360 Horz Pixels\r
300         DW  06B00H      ; Horz total\r
301         DW  05901H      ; Horz Displayed\r
302         DW  05A02H      ; Start Horz Blanking\r
303         DW  08E03H      ; End Horz Blanking\r
304         DW  05E04H      ; Start H Sync\r
305         DW  08A05H      ; End H Sync\r
306         DW  nil         ; End of CRTC Data for 360 Horz pixels\r
307 \r
308 MODE_200_Tall:\r
309 MODE_400_Tall:          ; CRTC Setup Data for 200/400 Line modes\r
310         DW  0BF06H      ; Vertical Total\r
311         DW  01F07H      ; Overflow\r
312         DW  09C10H      ; V Sync Start\r
313         DW  08E11H      ; V Sync End/Prot Cr0 Cr7\r
314         DW  08F12H      ; Vertical Displayed\r
315         DW  09615H      ; V Blank Start\r
316         DW  0B916H      ; V Blank End\r
317         DW  nil         ; End of CRTC Data for 200/400 Lines\r
318 \r
319 MODE_240_Tall:\r
320 MODE_480_Tall:          ; CRTC Setup Data for 240/480 Line modes\r
321         DW  00D06H      ; Vertical Total\r
322         DW  03E07H      ; Overflow\r
323         DW  0EA10H      ; V Sync Start\r
324         DW  08C11H      ; V Sync End/Prot Cr0 Cr7\r
325         DW  0DF12H      ; Vertical Displayed\r
326         DW  0E715H      ; V Blank Start\r
327         DW  00616H      ; V Blank End\r
328         DW  nil         ; End of CRTC Data for 240/480 Lines\r
329 \r
330         ; Table of Display Mode Tables\r
331 \r
332 MODE_TABLE:\r
333         DW  o MODE_320x200, o MODE_320x400\r
334         DW  o MODE_360x200, o MODE_360x400\r
335         DW  o MODE_320x240, o MODE_320x480\r
336         DW  o MODE_360x240, o MODE_360x480\r
337 \r
338         ; Table of Display Mode Components\r
339 \r
340 MODE_320x200:           ; Data for 320 by 200 Pixels\r
341 \r
342         DB  063h        ; 400 scan Lines & 25 Mhz Clock\r
343         DB  4           ; Maximum of 4 Pages\r
344         DW  320, 200    ; Displayed Pixels (X,Y)\r
345         DW  1302, 816   ; Max Possible X and Y Sizes\r
346 \r
347         DW  o MODE_320_Wide, o MODE_200_Tall\r
348         DW  o MODE_Double_Line, nil\r
349 \r
350 MODE_320x400:           ; Data for 320 by 400 Pixels\r
351 \r
352         DB  063h        ; 400 scan Lines & 25 Mhz Clock\r
353         DB  2           ; Maximum of 2 Pages\r
354         DW  320, 400    ; Displayed Pixels X,Y\r
355         DW  648, 816    ; Max Possible X and Y Sizes\r
356 \r
357         DW  o MODE_320_Wide, o MODE_400_Tall\r
358         DW  o MODE_Single_Line, nil\r
359 \r
360 MODE_360x240:           ; Data for 360 by 240 Pixels\r
361 \r
362         DB  0E7h        ; 480 scan Lines & 28 Mhz Clock\r
363         DB  3           ; Maximum of 3 Pages\r
364         DW  360, 240    ; Displayed Pixels X,Y\r
365         DW  1092, 728   ; Max Possible X and Y Sizes\r
366 \r
367         DW  o MODE_360_Wide, o MODE_240_Tall\r
368         DW  o MODE_Double_Line , nil\r
369 \r
370 MODE_360x480:           ; Data for 360 by 480 Pixels\r
371 \r
372         DB  0E7h        ; 480 scan Lines & 28 Mhz Clock\r
373         DB  1           ; Only 1 Page Possible\r
374         DW  360, 480    ; Displayed Pixels X,Y\r
375         DW  544, 728    ; Max Possible X and Y Sizes\r
376 \r
377         DW  o MODE_360_Wide, o MODE_480_Tall\r
378         DW  o MODE_Single_Line , nil\r
379 \r
380 MODE_320x240:           ; Data for 320 by 240 Pixels\r
381 \r
382         DB  0E3h        ; 480 scan Lines & 25 Mhz Clock\r
383         DB  3           ; Maximum of 3 Pages\r
384         DW  320, 240    ; Displayed Pixels X,Y\r
385         DW  1088, 818   ; Max Possible X and Y Sizes\r
386 \r
387         DW  o MODE_320_Wide, o MODE_240_Tall\r
388         DW  o MODE_Double_Line, nil\r
389 \r
390 MODE_320x480:           ; Data for 320 by 480 Pixels\r
391 \r
392         DB  0E3h        ; 480 scan Lines & 25 Mhz Clock\r
393         DB  1           ; Only 1 Page Possible\r
394         DW  320, 480    ; Displayed Pixels X,Y\r
395         DW  540, 818    ; Max Possible X and Y Sizes\r
396 \r
397         DW  o MODE_320_WIDE, o MODE_480_Tall\r
398         DW  o MODE_Single_Line, nil\r
399 \r
400 MODE_360x200:           ; Data for 360 by 200 Pixels\r
401 \r
402         DB  067h        ; 400 scan Lines & 28 Mhz Clock\r
403         DB  3           ; Maximum of 3 Pages\r
404         DW  360, 200    ; Displayed Pixels (X,Y)\r
405         DW  1302, 728   ; Max Possible X and Y Sizes\r
406 \r
407         DW  o MODE_360_Wide, MODE_200_Tall\r
408         DW  o MODE_Double_Line, nil\r
409 \r
410 MODE_360x400:           ; Data for 360 by 400 Pixels\r
411 \r
412         DB  067h        ; 400 scan Lines & 28 Mhz Clock\r
413         DB  1           ; Maximum of 1 Pages\r
414         DW  360, 400    ; Displayed Pixels X,Y\r
415         DW  648, 816    ; Max Possible X and Y Sizes\r
416 \r
417         DW  o MODE_360_Wide, MODE_400_Tall\r
418         DW  o MODE_Single_Line, nil\r
419 \r
420 \r
421     ; ===== MODE X SETUP ROUTINES =====\r
422 \r
423 ;======================================================\r
424 ;SET_VGA_MODEX% (ModeType%, MaxXPos%, MaxYpos%, Pages%)\r
425 ;======================================================\r
426 ;\r
427 ; Sets Up the specified version of Mode X.  Allows for\r
428 ; the setup of multiple video pages, and a virtual\r
429 ; screen which can be larger than the displayed screen\r
430 ; (which can then be scrolled a pixel at a time)\r
431 ;\r
432 ; ENTRY: ModeType = Desired Screen Resolution (0-7)\r
433 ;\r
434 ;     0 =  320 x 200, 4 Pages max,   1.2:1 Aspect Ratio\r
435 ;     1 =  320 x 400, 2 Pages max,   2.4:1 Aspect Ratio\r
436 ;     2 =  360 x 200, 3 Pages max,  1.35:1 Aspect Ratio\r
437 ;     3 =  360 x 400, 1 Page  max,   2.7:1 Aspect Ratio\r
438 ;     4 =  320 x 240, 3 Pages max,     1:1 Aspect Ratio\r
439 ;     5 =  320 x 480, 1 Page  max,     2:1 Aspect Ratio\r
440 ;     6 =  360 x 240, 3 Pages max, 1.125:1 Aspect Ratio\r
441 ;     7 =  360 x 480, 1 Page  max,  2.25:1 Aspect Ratio\r
442 ;\r
443 ;        MaxXpos = The Desired Virtual Screen Width\r
444 ;        MaxYpos = The Desired Virtual Screen Height\r
445 ;        Pages   = The Desired # of Video Pages\r
446 ;\r
447 ; EXIT:  AX = Success Flag:   >0 = Failure / 0 = Success\r
448 ;\r
449 \r
450 SVM_STACK   STRUC\r
451     SVM_Table   DW  ?   ; Offset of Mode Info Table\r
452                 DW  ?x1 ; DI, SI, DS, BP\r
453                 DW  ?x1 ; DI, SI, DS, BP\r
454                 DW  ?x1 ; DI, SI, DS, BP\r
455                 DW  ?x1 ; DI, SI, DS, BP\r
456                 DD  ?   ; Caller\r
457     SVM_Pages   DW  ?   ; # of Screen Pages desired\r
458     SVM_Ysize   DW  ?   ; Vertical Screen Size Desired\r
459     SVM_Xsize   DW  ?   ; Horizontal Screen Size Desired\r
460     SVM_Mode    DW  ?   ; Display Resolution Desired\r
461 SVM_STACK   ENDS\r
462 \r
463     PUBLIC  SET_VGA_MODEX\r
464 \r
465 SET_VGA_MODEX   PROC    FAR\r
466 \r
467     ;PUSHx   BP, DS, SI, DI      ; Preserve Important Registers\r
468     push        bp\r
469         push    ds\r
470         push    si\r
471         push    di\r
472     SUB     SP, 2               ; Allocate workspace\r
473     MOV     BP, SP              ; Set up Stack Frame\r
474 \r
475     ; Check Legality of Mode Request....\r
476 \r
477     MOV     BX, [BP].SVM_Mode   ; Get Requested Mode #\r
478     CMP     BX, NUM_MODES       ; Is it 0..7?\r
479     JAE     @SVM_BadModeSetup1   ; If Not, Error out\r
480 \r
481     SHL     BX, 1                   ; Scale BX\r
482     MOV     SI, w MODE_TABLE[BX]    ; CS:SI -> Mode Info\r
483     MOV     [BP].SVM_Table, SI      ; Save ptr for later use\r
484 \r
485     ; Check # of Requested Display Pages\r
486 \r
487     MOV     CX, [BP].SVM_Pages  ; Get # of Requested Pages\r
488     ;CLR        CH                  ; Set Hi Word = 0!\r
489     mov ch,0                  ; Set Hi Word = 0!\r
490     CMP     CL, CS:[SI].M_Pages ; Check # Pages for mode\r
491     JA      @SVM_BadModeSetup2   ; Report Error if too Many Pages\r
492     JCXZ    @SVM_BadModeSetup3   ; Report Error if 0 Pages\r
493 \r
494     ; Check Validity of X Size\r
495 \r
496     AND     [BP].SVM_XSize, 0FFF8h  ; X size Mod 8 Must = 0\r
497 \r
498     MOV     AX, [BP].SVM_XSize  ; Get Logical Screen Width\r
499     CMP     AX, CS:[SI].M_XSize ; Check against Displayed X\r
500     JB      @SVM_BadModeSetup4   ; Report Error if too small\r
501     CMP     AX, CS:[SI].M_XMax  ; Check against Max X\r
502     JA      @SVM_BadModeSetup5   ; Report Error if too big\r
503 \r
504     ; Check Validity of Y Size\r
505 \r
506     MOV     BX, [BP].SVM_YSize  ; Get Logical Screen Height\r
507     CMP     BX, CS:[SI].M_YSize ; Check against Displayed Y\r
508     JB      @SVM_BadModeSetup6   ; Report Error if too small\r
509     CMP     BX, CS:[SI].M_YMax  ; Check against Max Y\r
510     JA      @SVM_BadModeSetup7   ; Report Error if too big\r
511 \r
512     ; Enough memory to Fit it all?\r
513 \r
514     SHR     AX, 1               ; # of Bytes:Line = XSize/4\r
515     SHR     AX, 1               ; # of Bytes:Line = XSize/4\r
516     MUL     CX                  ; AX = Bytes/Line * Pages\r
517     MUL     BX                  ; DX:AX = Total VGA mem needed\r
518     JNO     @SVM_Continue       ; Exit if Total Size > 256K\r
519 \r
520     DEC     DX                  ; Was it Exactly 256K???\r
521     OR      DX, AX              ; (DX = 1, AX = 0000)\r
522     JZ      @SVM_Continue       ; if so, it's valid...\r
523 \r
524         jmp      @SVM_Continue;0000\r
525 \r
526 @SVM_BadModeSetup:\r
527     mov ax,8                  ; Return Value = False\r
528     JMP     @SVM_Exit           ; Normal Exit\r
529 @SVM_BadModeSetup1:\r
530     mov ax,1                  ; Return Value = False\r
531     JMP     @SVM_Exit           ; Normal Exit\r
532 @SVM_BadModeSetup2:\r
533     mov ax,2                  ; Return Value = False\r
534     JMP     @SVM_Exit           ; Normal Exit\r
535 @SVM_BadModeSetup3:\r
536     mov ax,3                  ; Return Value = False\r
537     JMP     @SVM_Exit           ; Normal Exit\r
538 @SVM_BadModeSetup4:\r
539     mov ax,4                  ; Return Value = False\r
540     JMP     @SVM_Exit           ; Normal Exit\r
541 @SVM_BadModeSetup5:\r
542     mov ax,5                  ; Return Value = False\r
543     JMP     @SVM_Exit           ; Normal Exit\r
544 @SVM_BadModeSetup6:\r
545     mov ax,6                  ; Return Value = False\r
546     JMP     @SVM_Exit           ; Normal Exit\r
547 @SVM_BadModeSetup7:\r
548     mov ax,7                  ; Return Value = False\r
549     JMP     @SVM_Exit           ; Normal Exit\r
550 \r
551 @SVM_Continue:\r
552 \r
553     MOV     AX, 13H             ; Start with Mode 13H\r
554     INT     10H                 ; Let BIOS Set Mode\r
555 \r
556     OUT_16  SC_INDEX, CHAIN4_OFF            ; Disable Chain 4 Mode\r
557     OUT_16  SC_INDEX, ASYNC_RESET           ; (A)synchronous Reset\r
558     OUT_8   MISC_OUTPUT, CS:[SI].M_MiscR    ; Set New Timing/Size\r
559     OUT_16  SC_INDEX, SEQU_RESTART          ; Restart Sequencer ...\r
560 \r
561     OUT_8   CRTC_INDEX, 11H     ; Select Vert Retrace End Register\r
562     INC     DX                  ; Point to Data\r
563     IN      AL, DX              ; Get Value, Bit 7 = Protect\r
564     AND     AL, 7FH             ; Mask out Write Protect\r
565     OUT     DX, AL              ; And send it back\r
566 \r
567     MOV     DX, CRTC_INDEX      ; Vga Crtc Registers\r
568     ADD     SI, M_CRTC          ; SI -> CRTC Parameter Data\r
569 \r
570     ; Load Tables of CRTC Parameters from List of Tables\r
571 \r
572 @SVM_Setup_Table:\r
573 \r
574     MOV     DI, CS:[SI]         ; Get Pointer to CRTC Data Tbl\r
575     ADD     SI, 2               ; Point to next Ptr Entry\r
576     OR      DI, DI              ; A nil Ptr means that we have\r
577     JZ      @SVM_Set_Data       ; finished CRTC programming\r
578 \r
579 @SVM_Setup_CRTC:\r
580     MOV     AX, CS:[DI]         ; Get CRTC Data from Table\r
581     ADD     DI, 2               ; Advance Pointer\r
582     OR      AX, AX              ; At End of Data Table?\r
583     JZ      @SVM_Setup_Table    ; If so, Exit & get next Table\r
584 \r
585     OUT     DX, AX              ; Reprogram VGA CRTC reg\r
586     JMP     s @SVM_Setup_CRTC   ; Process Next Table Entry\r
587 \r
588     ; Initialize Page & Scroll info, DI = 0\r
589 \r
590 @SVM_Set_Data:\r
591     MOV     DISPLAY_PAGE, DI    ; Display Page = 0\r
592     MOV     ACTIVE_PAGE, DI     ; Active Page = 0\r
593     MOV     CURRENT_PAGE, DI    ; Current Page (Offset) = 0\r
594     MOV     CURRENT_XOFFSET, DI ; Horz Scroll Index = 0\r
595     MOV     CURRENT_YOFFSET, DI ; Vert Scroll Index = 0\r
596     MOV     CURRENT_MOFFSET, DI ; Memory Scroll Index = 0\r
597 \r
598     MOV     AX, VGA_SEGMENT     ; Segment for VGA memory\r
599     MOV     CURRENT_SEGMENT, AX ; Save for Future LES's\r
600 \r
601     ; Set Logical Screen Width, X Scroll and Our Data\r
602 \r
603     MOV     SI, [BP].SVM_Table  ; Get Saved Ptr to Mode Info\r
604     MOV     AX, [BP].SVM_Xsize  ; Get Display Width\r
605 \r
606     MOV     CX, AX              ; CX = Logical Width\r
607     SUB     CX, CS:[SI].M_XSize ; CX = Max X Scroll Value\r
608     MOV     MAX_XOFFSET, CX     ; Set Maximum X Scroll\r
609 \r
610     SHR     AX, 1               ; Bytes = Pixels / 4\r
611     SHR     AX, 1               ; Bytes = Pixels / 4\r
612     MOV     SCREEN_WIDTH, AX    ; Save Width in Pixels\r
613 \r
614     SHR     AX, 1               ; Offset Value = Bytes / 2\r
615     MOV     AH, 13h             ; CRTC Offset Register Index\r
616     XCHG    AL, AH              ; Switch format for OUT\r
617     OUT     DX, AX              ; Set VGA CRTC Offset Reg\r
618 \r
619     ; Setup Data table, Y Scroll, Misc for Other Routines\r
620 \r
621     MOV     AX, [BP].SVM_Ysize  ; Get Logical Screen Height\r
622 \r
623     MOV     CX, AX              ; CX = Logical Height\r
624     SUB     BX, CS:[SI].M_YSize ; CX = Max Y Scroll Value\r
625     MOV     MAX_YOFFSET, CX     ; Set Maximum Y Scroll\r
626 \r
627     MOV     SCREEN_HEIGHT, AX   ; Save Height in Pixels\r
628     MUL     SCREEN_WIDTH        ; AX = Page Size in Bytes,\r
629     MOV     PAGE_SIZE, AX       ; Save Page Size\r
630 \r
631     MOV     CX, [BP].SVM_Pages  ; Get # of Pages\r
632     MOV     LAST_PAGE, CX       ; Save # of Pages\r
633 \r
634     mov bx,0                  ; Page # = 0\r
635     MOV     DX, BX              ; Page 0 Offset = 0\r
636 \r
637 @SVM_Set_Pages:\r
638 \r
639     MOV     PAGE_ADDR[BX], DX   ; Set Page #(BX) Offset\r
640     ADD     BX, 2               ; Page#++\r
641     ADD     DX, AX              ; Compute Addr of Next Page\r
642     LOOPx   CX, @SVM_Set_Pages  ; Loop until all Pages Set\r
643 \r
644     ; Clear VGA Memory\r
645 \r
646     OUT_16  SC_INDEX, ALL_PLANES_ON ; Select All Planes\r
647     LES     DI, d CURRENT_PAGE      ; -> Start of VGA memory\r
648 \r
649     mov ax,0                  ; AX = 0\r
650     CLD                         ; Block Xfer Forwards\r
651     MOV     CX, 8000H           ; 32K * 4 * 2 = 256K\r
652     REP     STOSW               ; Clear dat memory!\r
653 \r
654     ; Setup Font Pointers\r
655 \r
656     MOV     BH, ROM_8x8_Lo      ; Ask for 8x8 Font, 0-127\r
657     MOV     AX, GET_CHAR_PTR    ; Service to Get Pointer\r
658     INT     10h                 ; Call VGA BIOS\r
659 \r
660     MOV     CHARSET_LOW, BP     ; Save Char Set Offset\r
661     MOV     CHARSET_LOW+2, ES   ; Save Char Set Segment\r
662 \r
663     MOV     BH, ROM_8x8_Hi      ; Ask for 8x8 Font, 128-255\r
664     MOV     AX, GET_CHAR_PTR    ; Service to Get Pointer\r
665     INT     10h                 ; Call VGA BIOS\r
666 \r
667     MOV     CHARSET_HI, BP      ; Save Char Set Offset\r
668     MOV     CHARSET_HI+2, ES    ; Save Char Set Segment\r
669 \r
670     MOV     AX, True            ; Return Success Code\r
671 \r
672 @SVM_EXIT:\r
673     ADD     SP, 2               ; Deallocate workspace\r
674     ;POPx    DI, SI, DS, BP      ; Restore Saved Registers\r
675     pop di\r
676         pop     si\r
677         pop     ds\r
678         pop     bp\r
679     RET     8                   ; Exit & Clean Up Stack\r
680 \r
681 SET_VGA_MODEX   ENDP\r
682 \r
683 \r
684 ;==================\r
685 ;SET_MODEX% (Mode%)\r
686 ;==================\r
687 ;\r
688 ; Quickie Mode Set - Sets Up Mode X to Default Configuration\r
689 ;\r
690 ; ENTRY: ModeType = Desired Screen Resolution (0-7)\r
691 ;        (See SET_VGA_MODEX for list)\r
692 ;\r
693 ; EXIT:  AX = Success Flag:   0 = Failure / -1= Success\r
694 ;\r
695 \r
696 SM_STACK    STRUC\r
697                 DW  ?,? ; BP, SI\r
698                 DD  ?   ; Caller\r
699     SM_Mode     DW  ?   ; Desired Screen Resolution\r
700 SM_STACK    ENDS\r
701 \r
702     PUBLIC  SET_MODEX\r
703 \r
704 SET_MODEX   PROC    FAR\r
705 \r
706     ;PUSHx   BP, SI              ; Preserve Important registers\r
707     push        bp\r
708         push    si\r
709     MOV     BP, SP              ; Set up Stack Frame\r
710 \r
711     mov ax,0                  ; Assume Failure\r
712     MOV     BX, [BP].SM_Mode    ; Get Desired Mode #\r
713     CMP     BX, NUM_MODES       ; Is it a Valid Mode #?\r
714     JAE     @SMX_Exit           ; If Not, don't Bother\r
715 \r
716     PUSH    BX                  ; Push Mode Parameter\r
717 \r
718     SHL     BX, 1                   ; Scale BX to word Index\r
719     MOV     SI, w MODE_TABLE[BX]    ; CS:SI -> Mode Info\r
720 \r
721     PUSH    CS:[SI].M_XSize     ; Push Default X Size\r
722     PUSH    CS:[SI].M_Ysize     ; Push Default Y size\r
723     MOV     AL, CS:[SI].M_Pages ; Get Default # of Pages\r
724     mov ah,0                  ; Hi Byte = 0\r
725     PUSH    AX                  ; Push # Pages\r
726 \r
727     CALL    f SET_VGA_MODEX     ; Set up Mode X!\r
728 \r
729 @SMX_Exit:\r
730     ;POPx    SI, BP              ; Restore Registers\r
731         pop     si\r
732         pop     bp\r
733     RET     2                   ; Exit & Clean Up Stack\r
734 \r
735 SET_MODEX   ENDP\r
736 \r
737 \r
738     ; ===== BASIC GRAPHICS PRIMITIVES =====\r
739 \r
740 ;============================\r
741 ;CLEAR_VGA_SCREEN (ColorNum%)\r
742 ;============================\r
743 ;\r
744 ; Clears the active display page\r
745 ;\r
746 ; ENTRY: ColorNum = Color Value to fill the page with\r
747 ;\r
748 ; EXIT:  No meaningful values returned\r
749 ;\r
750 \r
751 CVS_STACK   STRUC\r
752                 DW  ?,? ; DI, BP\r
753                 DD  ?   ; Caller\r
754     CVS_COLOR   DB  ?,? ; Color to Set Screen to\r
755 CVS_STACK   ENDS\r
756 \r
757     PUBLIC  CLEAR_VGA_SCREEN\r
758 \r
759 CLEAR_VGA_SCREEN    PROC    FAR\r
760 \r
761     ;PUSHx   BP, DI              ; Preserve Important Registers\r
762     push        bp\r
763         push    di\r
764     MOV     BP, SP              ; Set up Stack Frame\r
765 \r
766     OUT_16  SC_INDEX, ALL_PLANES_ON ; Select All Planes\r
767     LES     DI, d CURRENT_PAGE      ; Point to Active VGA Page\r
768 \r
769     MOV     AL, [BP].CVS_COLOR  ; Get Color\r
770     MOV     AH, AL              ; Copy for Word Write\r
771     CLD                         ; Block fill Forwards\r
772 \r
773     MOV     CX, PAGE_SIZE       ; Get Size of Page\r
774     SHR     CX, 1               ; Divide by 2 for Words\r
775     REP     STOSW               ; Block Fill VGA memory\r
776 \r
777     ;POPx    DI, BP              ; Restore Saved Registers\r
778     pop di\r
779         pop     bp\r
780     RET     2                   ; Exit & Clean Up Stack\r
781 \r
782 CLEAR_VGA_SCREEN    ENDP\r
783 \r
784 \r
785 ;===================================\r
786 ;SET_POINT (Xpos%, Ypos%, ColorNum%)\r
787 ;===================================\r
788 ;\r
789 ; Plots a single Pixel on the active display page\r
790 ;\r
791 ; ENTRY: Xpos     = X position to plot pixel at\r
792 ;        Ypos     = Y position to plot pixel at\r
793 ;        ColorNum = Color to plot pixel with\r
794 ;\r
795 ; EXIT:  No meaningful values returned\r
796 ;\r
797 \r
798 SP_STACK    STRUC\r
799                 DW  ?,? ; BP, DI\r
800                 DD  ?   ; Caller\r
801     SETP_Color  DB  ?,? ; Color of Point to Plot\r
802     SETP_Ypos   DW  ?   ; Y pos of Point to Plot\r
803     SETP_Xpos   DW  ?   ; X pos of Point to Plot\r
804 SP_STACK    ENDS\r
805 \r
806         PUBLIC SET_POINT\r
807 \r
808 SET_POINT   PROC    FAR\r
809 \r
810     ;PUSHx   BP, DI              ; Preserve Registers\r
811     push        bp\r
812         push    di\r
813     MOV     BP, SP              ; Set up Stack Frame\r
814 \r
815     LES     DI, d CURRENT_PAGE  ; Point to Active VGA Page\r
816 \r
817     MOV     AX, [BP].SETP_Ypos  ; Get Line # of Pixel\r
818     MUL     SCREEN_WIDTH        ; Get Offset to Start of Line\r
819 \r
820     MOV     BX, [BP].SETP_Xpos  ; Get Xpos\r
821     MOV     CX, BX              ; Copy to extract Plane # from\r
822     SHR     BX, 1               ; X offset (Bytes) = Xpos/4\r
823     SHR     BX, 1               ; X offset (Bytes) = Xpos/4\r
824     ADD     BX, AX              ; Offset = Width*Ypos + Xpos/4\r
825 \r
826     MOV     AX, MAP_MASK_PLANE1 ; Map Mask & Plane Select Register\r
827     AND     CL, PLANE_BITS      ; Get Plane Bits\r
828     SHL     AH, CL              ; Get Plane Select Value\r
829     OUT_16  SC_Index, AX        ; Select Plane\r
830 \r
831     MOV     AL,[BP].SETP_Color  ; Get Pixel Color\r
832     MOV     ES:[DI+BX], AL      ; Draw Pixel\r
833 \r
834     ;POPx    DI, BP              ; Restore Saved Registers\r
835     pop di\r
836         pop     bp\r
837     RET     6                   ; Exit and Clean up Stack\r
838 \r
839 SET_POINT        ENDP\r
840 \r
841 \r
842 ;==========================\r
843 ;READ_POINT% (Xpos%, Ypos%)\r
844 ;==========================\r
845 ;\r
846 ; Read the color of a pixel from the Active Display Page\r
847 ;\r
848 ; ENTRY: Xpos = X position of pixel to read\r
849 ;        Ypos = Y position of pixel to read\r
850 ;\r
851 ; EXIT:  AX   = Color of Pixel at (Xpos, Ypos)\r
852 ;\r
853 \r
854 RP_STACK    STRUC\r
855             DW  ?,? ; BP, DI\r
856             DD  ?   ; Caller\r
857     RP_Ypos DW  ?   ; Y pos of Point to Read\r
858     RP_Xpos DW  ?   ; X pos of Point to Read\r
859 RP_STACK    ENDS\r
860 \r
861         PUBLIC  READ_POINT\r
862 \r
863 READ_POINT      PROC    FAR\r
864 \r
865     ;PUSHx   BP, DI              ; Preserve Registers\r
866     push        bp\r
867         push    di\r
868     MOV     BP, SP              ; Set up Stack Frame\r
869 \r
870     LES     DI, d CURRENT_PAGE  ; Point to Active VGA Page\r
871 \r
872     MOV     AX, [BP].RP_Ypos    ; Get Line # of Pixel\r
873     MUL     SCREEN_WIDTH        ; Get Offset to Start of Line\r
874 \r
875     MOV     BX, [BP].RP_Xpos    ; Get Xpos\r
876     MOV     CX, BX\r
877     SHR     BX, 1               ; X offset (Bytes) = Xpos/4\r
878     SHR     BX, 1               ; X offset (Bytes) = Xpos/4\r
879     ADD     BX, AX              ; Offset = Width*Ypos + Xpos/4\r
880 \r
881     MOV     AL, READ_MAP        ; GC Read Mask Register\r
882     MOV     AH, CL              ; Get Xpos\r
883     AND     AH, PLANE_BITS      ; & mask out Plane #\r
884     OUT_16  GC_INDEX, AX        ; Select Plane to read in\r
885 \r
886     mov ah,0                  ; Clear Return Value Hi byte\r
887     MOV     AL, ES:[DI+BX]      ; Get Color of Pixel\r
888 \r
889     ;POPx    DI, BP              ; Restore Saved Registers\r
890     pop di\r
891         pop     bp\r
892     RET     4                   ; Exit and Clean up Stack\r
893 \r
894 READ_POINT        ENDP\r
895 \r
896 \r
897 ;======================================================\r
898 ;FILL_BLOCK (Xpos1%, Ypos1%, Xpos2%, Ypos2%, ColorNum%)\r
899 ;======================================================\r
900 ;\r
901 ; Fills a rectangular block on the active display Page\r
902 ;\r
903 ; ENTRY: Xpos1    = Left X position of area to fill\r
904 ;        Ypos1    = Top Y position of area to fill\r
905 ;        Xpos2    = Right X position of area to fill\r
906 ;        Ypos2    = Bottom Y position of area to fill\r
907 ;        ColorNum = Color to fill area with\r
908 ;\r
909 ; EXIT:  No meaningful values returned\r
910 ;\r
911 \r
912 FB_STACK    STRUC\r
913                 DW  ?x1 ; DS, DI, SI, BP\r
914                 DW  ?x1 ; DS, DI, SI, BP\r
915                 DW  ?x1 ; DS, DI, SI, BP\r
916                 DW  ?x1 ; DS, DI, SI, BP\r
917                 DD  ?   ; Caller\r
918     FB_Color    DB  ?,? ; Fill Color\r
919     FB_Ypos2    DW  ?   ; Y pos of Lower Right Pixel\r
920     FB_Xpos2    DW  ?   ; X pos of Lower Right Pixel\r
921     FB_Ypos1    DW  ?   ; Y pos of Upper Left Pixel\r
922     FB_Xpos1    DW  ?   ; X pos of Upper Left Pixel\r
923 FB_STACK    ENDS\r
924 \r
925         PUBLIC    FILL_BLOCK\r
926 \r
927 FILL_BLOCK  PROC    FAR\r
928 \r
929     ;PUSHx   BP, DS, SI, DI      ; Preserve Important Registers\r
930     push        bp\r
931         push    ds\r
932         push    si\r
933         push    di\r
934     MOV     BP, SP              ; Set up Stack Frame\r
935 \r
936     LES     DI, d CURRENT_PAGE  ; Point to Active VGA Page\r
937     CLD                         ; Direction Flag = Forward\r
938 \r
939     OUT_8   SC_INDEX, MAP_MASK  ; Set up for Plane Select\r
940 \r
941     ; Validate Pixel Coordinates\r
942     ; If necessary, Swap so X1 <= X2, Y1 <= Y2\r
943 \r
944     MOV     AX, [BP].FB_Ypos1   ; AX = Y1   is Y1< Y2?\r
945     MOV     BX, [BP].FB_Ypos2   ; BX = Y2\r
946     CMP     AX, BX\r
947     JLE     @FB_NOSWAP1\r
948 \r
949     MOV     [BP].FB_Ypos1, BX   ; Swap Y1 and Y2 and save Y1\r
950     XCHG    AX, BX              ; on stack for future use\r
951 \r
952 @FB_NOSWAP1:\r
953     SUB     BX, AX              ; Get Y width\r
954     INC     BX                  ; Add 1 to avoid 0 value\r
955     MOV     [BP].FB_Ypos2, BX   ; Save in Ypos2\r
956 \r
957     MUL     SCREEN_WIDTH        ; Mul Y1 by Bytes per Line\r
958     ADD     DI, AX              ; DI = Start of Line Y1\r
959 \r
960     MOV     AX, [BP].FB_Xpos1   ; Check X1 <= X2\r
961     MOV     BX, [BP].FB_Xpos2   ;\r
962     CMP     AX, BX\r
963     JLE     @FB_NOSWAP2         ; Skip Ahead if Ok\r
964 \r
965     MOV     [BP].FB_Xpos2, AX   ; Swap X1 AND X2 and save X2\r
966     XCHG    AX, BX              ; on stack for future use\r
967 \r
968     ; All our Input Values are in order, Now determine\r
969     ; How many full "bands" 4 pixels wide (aligned) there\r
970     ; are, and if there are partial bands (<4 pixels) on\r
971     ; the left and right edges.\r
972 \r
973 @FB_NOSWAP2:\r
974     MOV     DX, AX              ; DX = X1 (Pixel Position)\r
975     SHR     DX, 1               ; DX/4 = Bytes into Line\r
976     SHR     DX, 1               ; DX/4 = Bytes into Line\r
977     ADD     DI, DX              ; DI = Addr of Upper-Left Corner\r
978 \r
979     MOV     CX, BX              ; CX = X2 (Pixel Position)\r
980     SHR     CX, 1               ; CX/4 = Bytes into Line\r
981     SHR     CX, 1               ; CX/4 = Bytes into Line\r
982 \r
983     CMP     DX, CX              ; Start and end in same band?\r
984     JNE     @FB_NORMAL          ; if not, check for l & r edges\r
985     JMP     @FB_ONE_BAND_ONLY   ; if so, then special processing\r
986 \r
987 @FB_NORMAL:\r
988     SUB     CX, DX              ; CX = # bands -1\r
989     MOV     SI, AX              ; SI = PLANE#(X1)\r
990     AND     SI, PLANE_BITS      ; if Left edge is aligned then\r
991     JZ      @FB_L_PLANE_FLUSH   ; no special processing..\r
992 \r
993     ; Draw "Left Edge" vertical strip of 1-3 pixels...\r
994 \r
995     OUT_8   SC_Data, Left_Clip_Mask[SI] ; Set Left Edge Plane Mask\r
996 \r
997     MOV     SI, DI              ; SI = Copy of Start Addr (UL)\r
998 \r
999     MOV     DX, [BP].FB_Ypos2   ; Get # of Lines to draw\r
1000     MOV     AL, [BP].FB_Color   ; Get Fill Color\r
1001     MOV     BX, SCREEN_WIDTH    ; Get Vertical increment Value\r
1002 \r
1003 @FB_LEFT_LOOP:\r
1004     MOV     ES:[SI], AL         ; Fill in Left Edge Pixels\r
1005     ADD     SI, BX              ; Point to Next Line (Below)\r
1006     LOOPjz  DX, @FB_LEFT_CONT   ; Exit loop if all Lines Drawn\r
1007 \r
1008     MOV     ES:[SI], AL         ; Fill in Left Edge Pixels\r
1009     ADD     SI, BX              ; Point to Next Line (Below)\r
1010     LOOPx   DX, @FB_LEFT_LOOP   ; loop until left strip is drawn\r
1011 \r
1012 @FB_LEFT_CONT:\r
1013 \r
1014     INC     DI                  ; Point to Middle (or Right) Block\r
1015     DEC     CX                  ; Reset CX instead of JMP @FB_RIGHT\r
1016 \r
1017 @FB_L_PLANE_FLUSH:\r
1018     INC     CX                  ; Add in Left band to middle block\r
1019 \r
1020     ; DI = Addr of 1st middle Pixel (band) to fill\r
1021     ; CX = # of Bands to fill -1\r
1022 \r
1023 @FB_RIGHT:\r
1024     MOV     SI, [BP].FB_Xpos2   ; Get Xpos2\r
1025     AND     SI, PLANE_BITS      ; Get Plane values\r
1026     CMP     SI, 0003            ; Plane = 3?\r
1027     JE      @FB_R_EDGE_FLUSH    ; Hey, add to middle\r
1028 \r
1029     ; Draw "Right Edge" vertical strip of 1-3 pixels...\r
1030 \r
1031     OUT_8   SC_Data, Right_Clip_Mask[SI]    ; Right Edge Plane Mask\r
1032 \r
1033     MOV     SI, DI              ; Get Addr of Left Edge\r
1034     ADD     SI, CX              ; Add Width-1 (Bands)\r
1035     DEC     SI                  ; To point to top of Right Edge\r
1036 \r
1037     MOV     DX, [BP].FB_Ypos2   ; Get # of Lines to draw\r
1038     MOV     AL, [BP].FB_Color   ; Get Fill Color\r
1039     MOV     BX, SCREEN_WIDTH    ; Get Vertical increment Value\r
1040 \r
1041 @FB_RIGHT_LOOP:\r
1042     MOV     ES:[SI], AL         ; Fill in Right Edge Pixels\r
1043     ADD     SI, BX              ; Point to Next Line (Below)\r
1044     LOOPjz  DX, @FB_RIGHT_CONT  ; Exit loop if all Lines Drawn\r
1045 \r
1046     MOV     ES:[SI], AL         ; Fill in Right Edge Pixels\r
1047     ADD     SI, BX              ; Point to Next Line (Below)\r
1048     LOOPx   DX, @FB_RIGHT_LOOP  ; loop until left strip is drawn\r
1049 \r
1050 @FB_RIGHT_CONT:\r
1051 \r
1052     DEC     CX                  ; Minus 1 for Middle bands\r
1053     JZ      @FB_EXIT            ; Uh.. no Middle bands...\r
1054 \r
1055 @FB_R_EDGE_FLUSH:\r
1056 \r
1057     ; DI = Addr of Upper Left block to fill\r
1058     ; CX = # of Bands to fill in (width)\r
1059 \r
1060     OUT_8   SC_Data, ALL_PLANES ; Write to All Planes\r
1061 \r
1062     MOV     DX, SCREEN_WIDTH    ; DX = DI Increment\r
1063     SUB     DX, CX              ;  = Screen_Width-# Planes Filled\r
1064 \r
1065     MOV     BX, CX              ; BX = Quick Refill for CX\r
1066     MOV     SI, [BP].FB_Ypos2   ; SI = # of Line to Fill\r
1067     MOV     AL, [BP].FB_Color   ; Get Fill Color\r
1068 \r
1069 @FB_MIDDLE_LOOP:\r
1070     REP     STOSB               ; Fill in entire line\r
1071 \r
1072     MOV     CX, BX              ; Recharge CX (Line Width)\r
1073     ADD     DI, DX              ; Point to start of Next Line\r
1074     LOOPx   SI, @FB_MIDDLE_LOOP ; Loop until all lines drawn\r
1075 \r
1076     JMP     s @FB_EXIT          ; Outa here\r
1077 \r
1078 @FB_ONE_BAND_ONLY:\r
1079     MOV     SI, AX                  ; Get Left Clip Mask, Save X1\r
1080     AND     SI, PLANE_BITS          ; Mask out Row #\r
1081     MOV     AL, Left_Clip_Mask[SI]  ; Get Left Edge Mask\r
1082     MOV     SI, BX                  ; Get Right Clip Mask, Save X2\r
1083     AND     SI, PLANE_BITS          ; Mask out Row #\r
1084     AND     AL, Right_Clip_Mask[SI] ; Get Right Edge Mask byte\r
1085 \r
1086     OUT_8   SC_Data, AL         ; Clip For Left & Right Masks\r
1087 \r
1088     MOV     CX, [BP].FB_Ypos2   ; Get # of Lines to draw\r
1089     MOV     AL, [BP].FB_Color   ; Get Fill Color\r
1090     MOV     BX, SCREEN_WIDTH    ; Get Vertical increment Value\r
1091 \r
1092 @FB_ONE_LOOP:\r
1093     MOV     ES:[DI], AL         ; Fill in Pixels\r
1094     ADD     DI, BX              ; Point to Next Line (Below)\r
1095     LOOPjz  CX, @FB_EXIT        ; Exit loop if all Lines Drawn\r
1096 \r
1097     MOV     ES:[DI], AL         ; Fill in Pixels\r
1098     ADD     DI, BX              ; Point to Next Line (Below)\r
1099     LOOPx   CX, @FB_ONE_LOOP    ; loop until left strip is drawn\r
1100 \r
1101 @FB_EXIT:\r
1102     ;POPx    DI, SI, DS, BP      ; Restore Saved Registers\r
1103     pop di\r
1104         pop     si\r
1105         pop     ds\r
1106         pop     bp\r
1107     RET     10                  ; Exit and Clean up Stack\r
1108 \r
1109 FILL_BLOCK   ENDP\r
1110 \r
1111 \r
1112 ;=====================================================\r
1113 ;DRAW_LINE (Xpos1%, Ypos1%, Xpos2%, Ypos2%, ColorNum%)\r
1114 ;=====================================================\r
1115 ;\r
1116 ; Draws a Line on the active display page\r
1117 ;\r
1118 ; ENTRY: Xpos1    = X position of first point on line\r
1119 ;        Ypos1    = Y position of first point on line\r
1120 ;        Xpos2    = X position of last point on line\r
1121 ;        Ypos2    = Y position of last point on line\r
1122 ;        ColorNum = Color to draw line with\r
1123 ;\r
1124 ; EXIT:  No meaningful values returned\r
1125 ;\r
1126 \r
1127 DL_STACK    STRUC\r
1128                 DW  ?x1 ; DI, SI, BP\r
1129                 DW  ?x1 ; DI, SI, BP\r
1130                 DW  ?x1 ; DI, SI, BP\r
1131                 DD  ?   ; Caller\r
1132     DL_ColorF   DB  ?,? ; Line Draw Color\r
1133     DL_Ypos2    DW  ?   ; Y pos of last point\r
1134     DL_Xpos2    DW  ?   ; X pos of last point\r
1135     DL_Ypos1    DW  ?   ; Y pos of first point\r
1136     DL_Xpos1    DW  ?   ; X pos of first point\r
1137 DL_STACK    ENDS\r
1138 \r
1139         PUBLIC DRAW_LINE\r
1140 \r
1141 DRAW_LINE   PROC    FAR\r
1142 \r
1143     ;PUSHx   BP, SI, DI          ; Preserve Important Registers\r
1144     push        bp\r
1145         push    si\r
1146         push    di\r
1147     MOV     BP, SP              ; Set up Stack Frame\r
1148     CLD                         ; Direction Flag = Forward\r
1149 \r
1150     OUT_8   SC_INDEX, MAP_MASK  ; Set up for Plane Select\r
1151     MOV     CH, [BP].DL_ColorF  ; Save Line Color in CH\r
1152 \r
1153     ; Check Line Type\r
1154 \r
1155     MOV     SI, [BP].DL_Xpos1   ; AX = X1   is X1< X2?\r
1156     MOV     DI, [BP].DL_Xpos2   ; DX = X2\r
1157     CMP     SI, DI              ; Is X1 < X2\r
1158     JE      @DL_VLINE           ; If X1=X2, Draw Vertical Line\r
1159     JL      @DL_NOSWAP1         ; If X1 < X2, don't swap\r
1160 \r
1161     XCHG    SI, DI              ; X2 IS > X1, SO SWAP THEM\r
1162 \r
1163 @DL_NOSWAP1:\r
1164 \r
1165     ; SI = X1, DI = X2\r
1166 \r
1167     MOV     AX, [BP].DL_Ypos1   ; AX = Y1   is Y1 <> Y2?\r
1168     CMP     AX, [BP].DL_Ypos2   ; Y1 = Y2?\r
1169     JE      @DL_HORZ            ; If so, Draw a Horizontal Line\r
1170 \r
1171     JMP     @DL_BREZHAM         ; Diagonal line... go do it...\r
1172 \r
1173     ; This Code draws a Horizontal Line in Mode X where:\r
1174     ; SI = X1, DI = X2, and AX = Y1/Y2\r
1175 \r
1176 @DL_HORZ:\r
1177 \r
1178     MUL     SCREEN_WIDTH        ; Offset = Ypos * Screen_Width\r
1179     MOV     DX, AX              ; CX = Line offset into Page\r
1180 \r
1181     MOV     AX, SI                  ; Get Left edge, Save X1\r
1182     AND     SI, PLANE_BITS          ; Mask out Row #\r
1183     MOV     BL, Left_Clip_Mask[SI]  ; Get Left Edge Mask\r
1184     MOV     CX, DI                  ; Get Right edge, Save X2\r
1185     AND     DI, PLANE_BITS          ; Mask out Row #\r
1186     MOV     BH, Right_Clip_Mask[DI] ; Get Right Edge Mask byte\r
1187 \r
1188     SHR     AX, 1               ; Get X1 Byte # (=X1/4)\r
1189     SHR     CX, 1               ; Get X2 Byte # (=X2/4)\r
1190     SHR     AX, 1               ; Get X1 Byte # (=X1/4)\r
1191     SHR     CX, 1               ; Get X2 Byte # (=X2/4)\r
1192 \r
1193     LES     DI, d CURRENT_PAGE  ; Point to Active VGA Page\r
1194     ADD     DI, DX              ; Point to Start of Line\r
1195     ADD     DI, AX              ; Point to Pixel X1\r
1196 \r
1197     SUB     CX, AX              ; CX = # Of Bands (-1) to set\r
1198     JNZ     @DL_LONGLN          ; jump if longer than one segment\r
1199 \r
1200     AND     BL, BH              ; otherwise, merge clip masks\r
1201 \r
1202 @DL_LONGLN:\r
1203 \r
1204     OUT_8   SC_Data, BL         ; Set the Left Clip Mask\r
1205 \r
1206     MOV     AL, [BP].DL_ColorF  ; Get Line Color\r
1207     MOV     BL, AL              ; BL = Copy of Line Color\r
1208     STOSB                       ; Set Left (1-4) Pixels\r
1209 \r
1210     JCXZ    @DL_EXIT            ; Done if only one Line Segment\r
1211 \r
1212     DEC     CX                  ; CX = # of Middle Segments\r
1213     JZ      @DL_XRSEG           ; If no middle segments....\r
1214 \r
1215     ; Draw Middle Segments\r
1216 \r
1217     OUT_8   DX, ALL_PLANES      ; Write to ALL Planes\r
1218 \r
1219     MOV     AL, BL              ; Get Color from BL\r
1220     REP     STOSB               ; Draw Middle (4 Pixel) Segments\r
1221 \r
1222 @DL_XRSEG:\r
1223     OUT_8   DX, BH              ; Select Planes for Right Clip Mask\r
1224     MOV     AL, BL              ; Get Color Value\r
1225     STOSB                       ; Draw Right (1-4) Pixels\r
1226 \r
1227     JMP     s @DL_EXIT          ; We Are Done...\r
1228 \r
1229 \r
1230     ; This Code Draws A Vertical Line.  On entry:\r
1231     ; CH = Line Color, SI & DI = X1\r
1232 \r
1233 @DL_VLINE:\r
1234 \r
1235     MOV     AX, [BP].DL_Ypos1   ; AX = Y1\r
1236     MOV     SI, [BP].DL_Ypos2   ; SI = Y2\r
1237     CMP     AX, SI              ; Is Y1 < Y2?\r
1238     JLE     @DL_NOSWAP2         ; if so, Don't Swap them\r
1239 \r
1240     XCHG    AX, SI              ; Ok, NOW Y1 < Y2\r
1241 \r
1242 @DL_NOSWAP2:\r
1243 \r
1244     SUB     SI, AX              ; SI = Line Height (Y2-Y1+1)\r
1245     INC     SI\r
1246 \r
1247     ; AX = Y1, DI = X1, Get offset into Page into AX\r
1248 \r
1249     MUL     SCREEN_WIDTH        ; Offset = Y1 (AX) * Screen Width\r
1250     MOV     DX, DI              ; Copy Xpos into DX\r
1251     SHR     DI, 1               ; DI = Xpos/4\r
1252     SHR     DI, 1               ; DI = Xpos/4\r
1253     ADD     AX, DI              ; DI = Xpos/4 + ScreenWidth * Y1\r
1254 \r
1255     LES     DI, d CURRENT_PAGE  ; Point to Active VGA Page\r
1256     ADD     DI, AX              ; Point to Pixel X1, Y1\r
1257 \r
1258     ;Select Plane\r
1259 \r
1260     MOV     CL, DL              ; CL = Save X1\r
1261     AND     CL, PLANE_BITS      ; Get X1 MOD 4 (Plane #)\r
1262     MOV     AX, MAP_MASK_PLANE1 ; Code to set Plane #1\r
1263     SHL     AH, CL              ; Change to Correct Plane #\r
1264     OUT_16  SC_Index, AX        ; Select Plane\r
1265 \r
1266     MOV     AL, CH              ; Get Saved Color\r
1267     MOV     BX, SCREEN_WIDTH    ; Get Offset to Advance Line By\r
1268 \r
1269 @DL_VLoop:\r
1270     MOV     ES:[DI], AL         ; Draw Single Pixel\r
1271     ADD     DI, BX              ; Point to Next Line\r
1272     LOOPjz  SI, @DL_EXIT        ; Lines--, Exit if done\r
1273 \r
1274     MOV     ES:[DI], AL         ; Draw Single Pixel\r
1275     ADD     DI, BX              ; Point to Next Line\r
1276     LOOPx   SI, @DL_VLoop       ; Lines--, Loop until Done\r
1277 \r
1278 @DL_EXIT:\r
1279 \r
1280     JMP     @DL_EXIT2           ; Done!\r
1281 \r
1282     ; This code Draws a diagonal line in Mode X\r
1283 \r
1284 @DL_BREZHAM:\r
1285     LES     DI, d CURRENT_PAGE  ; Point to Active VGA Page\r
1286 \r
1287     MOV     AX, [BP].DL_Ypos1   ; get Y1 value\r
1288     MOV     BX, [BP].DL_Ypos2   ; get Y2 value\r
1289     MOV     CX, [BP].DL_Xpos1   ; Get Starting Xpos\r
1290 \r
1291     CMP     BX, AX              ; Y2-Y1 is?\r
1292     JNC     @DL_DeltaYOK        ; if Y2>=Y1 then goto...\r
1293 \r
1294     XCHG    BX, AX              ; Swap em...\r
1295     MOV     CX, [BP].DL_Xpos2   ; Get New Starting Xpos\r
1296 \r
1297 @DL_DeltaYOK:\r
1298     MUL     SCREEN_WIDTH        ; Offset = SCREEN_WIDTH * Y1\r
1299 \r
1300     ADD     DI, AX              ; DI -> Start of Line Y1 on Page\r
1301     MOV     AX, CX              ; AX = Xpos (X1)\r
1302     SHR     AX, 1               ; /4 = Byte Offset into Line\r
1303     SHR     AX, 1               ; /4 = Byte Offset into Line\r
1304     ADD     DI, AX              ; DI = Starting pos (X1,Y1)\r
1305 \r
1306     MOV     AL, 11h             ; Staring Mask\r
1307     AND     CL, PLANE_BITS      ; Get Plane #\r
1308     SHL     AL, CL              ; and shift into place\r
1309     MOV     AH, [BP].DL_ColorF  ; Color in Hi Bytes\r
1310 \r
1311     PUSH    AX                  ; Save Mask,Color...\r
1312 \r
1313     MOV     AH, AL              ; Plane # in AH\r
1314     MOV     AL, MAP_MASK        ; Select Plane Register\r
1315     OUT_16  SC_Index, AX        ; Select initial plane\r
1316 \r
1317     MOV     AX, [BP].DL_Xpos1   ; get X1 value\r
1318     MOV     BX, [BP].DL_Ypos1   ; get Y1 value\r
1319     MOV     CX, [BP].DL_Xpos2   ; get X2 value\r
1320     MOV     DX, [BP].DL_Ypos2   ; get Y2 value\r
1321 \r
1322     MOV     BP, SCREEN_WIDTH    ; Use BP for Line width to\r
1323                                 ; to avoid extra memory access\r
1324 \r
1325     SUB     DX, BX              ; figure Delta_Y\r
1326     JNC     @DL_DeltaYOK2       ; jump if Y2 >= Y1\r
1327 \r
1328     ADD     BX, DX              ; put Y2 into Y1\r
1329     NEG     DX                  ; abs(Delta_Y)\r
1330     XCHG    AX, CX              ; and exchange X1 and X2\r
1331 \r
1332 @DL_DeltaYOK2:\r
1333     MOV     BX, 08000H          ; seed for fraction accumulator\r
1334 \r
1335     SUB     CX, AX              ; figure Delta_X\r
1336     JC      @DL_DrawLeft        ; if negative, go left\r
1337 \r
1338     JMP     @DL_DrawRight       ; Draw Line that slopes right\r
1339 \r
1340 @DL_DrawLeft:\r
1341 \r
1342     NEG     CX                  ; abs(Delta_X)\r
1343 \r
1344     CMP     CX, DX              ; is Delta_X < Delta_Y?\r
1345     JB      @DL_SteepLeft       ; yes, so go do steep line\r
1346                                 ; (Delta_Y iterations)\r
1347 \r
1348     ; Draw a Shallow line to the left in Mode X\r
1349 \r
1350 @DL_ShallowLeft:\r
1351     mov ax,0                  ; zero low word of Delta_Y * 10000h\r
1352     SUB     AX, DX              ; DX:AX <- DX * 0FFFFh\r
1353     SBB     DX, 0               ; include carry\r
1354     DIV     CX                  ; divide by Delta_X\r
1355 \r
1356     MOV     SI, BX              ; SI = Accumulator\r
1357     MOV     BX, AX              ; BX = Add fraction\r
1358     POP     AX                  ; Get Color, Bit mask\r
1359     MOV     DX, SC_Data         ; Sequence controller data register\r
1360     INC     CX                  ; Inc Delta_X so we can unroll loop\r
1361 \r
1362     ; Loop (x2) to Draw Pixels, Move Left, and Maybe Down...\r
1363 \r
1364 @DL_SLLLoop:\r
1365     MOV     ES:[DI], AH         ; set first pixel, plane data set up\r
1366     LOOPjz  CX, @DL_SLLExit     ; Delta_X--, Exit if done\r
1367 \r
1368     ADD     SI, BX              ; add numerator to accumulator\r
1369     JNC     @DL_SLLL2nc         ; move down on carry\r
1370 \r
1371     ADD     DI, BP              ; Move Down one line...\r
1372 \r
1373 @DL_SLLL2nc:\r
1374     DEC     DI                  ; Left one addr\r
1375     ROR     AL, 1               ; Move Left one plane, back on 0 1 2\r
1376     CMP     AL, 87h             ; wrap?, if AL <88 then Carry set\r
1377     ADC     DI, 0               ; Adjust Address: DI = DI + Carry\r
1378     OUT     DX, AL              ; Set up New Bit Plane mask\r
1379 \r
1380     MOV     ES:[DI], AH         ; set pixel\r
1381     LOOPjz  CX, @DL_SLLExit     ; Delta_X--, Exit if done\r
1382 \r
1383     ADD     SI, BX              ; add numerator to accumulator,\r
1384     JNC     @DL_SLLL3nc         ; move down on carry\r
1385 \r
1386     ADD     DI, BP              ; Move Down one line...\r
1387 \r
1388 @DL_SLLL3nc:                    ; Now move left a pixel...\r
1389     DEC     DI                  ; Left one addr\r
1390     ROR     AL, 1               ; Move Left one plane, back on 0 1 2\r
1391     CMP     AL, 87h             ; Wrap?, if AL <88 then Carry set\r
1392     ADC     DI, 0               ; Adjust Address: DI = DI + Carry\r
1393     OUT     DX, AL              ; Set up New Bit Plane mask\r
1394     JMP     s @DL_SLLLoop       ; loop until done\r
1395 \r
1396 @DL_SLLExit:\r
1397     JMP     @DL_EXIT2           ; and exit\r
1398 \r
1399     ; Draw a steep line to the left in Mode X\r
1400 \r
1401 @DL_SteepLeft:\r
1402     mov ax,0                  ; zero low word of Delta_Y * 10000h\r
1403     XCHG    DX, CX              ; Delta_Y switched with Delta_X\r
1404     DIV     CX                  ; divide by Delta_Y\r
1405 \r
1406     MOV     SI, BX              ; SI = Accumulator\r
1407     MOV     BX, AX              ; BX = Add Fraction\r
1408     POP     AX                  ; Get Color, Bit mask\r
1409     MOV     DX, SC_Data         ; Sequence controller data register\r
1410     INC     CX                  ; Inc Delta_Y so we can unroll loop\r
1411 \r
1412     ; Loop (x2) to Draw Pixels, Move Down, and Maybe left\r
1413 \r
1414 @DL_STLLoop:\r
1415 \r
1416     MOV     ES:[DI], AH         ; set first pixel\r
1417     LOOPjz  CX, @DL_STLExit     ; Delta_Y--, Exit if done\r
1418 \r
1419     ADD     SI, BX              ; add numerator to accumulator\r
1420     JNC     @DL_STLnc2          ; No carry, just move down!\r
1421 \r
1422     DEC     DI                  ; Move Left one addr\r
1423     ROR     AL, 1               ; Move Left one plane, back on 0 1 2\r
1424     CMP     AL, 87h             ; Wrap?, if AL <88 then Carry set\r
1425     ADC     DI, 0               ; Adjust Address: DI = DI + Carry\r
1426     OUT     DX, AL              ; Set up New Bit Plane mask\r
1427 \r
1428 @DL_STLnc2:\r
1429     ADD     DI, BP              ; advance to next line.\r
1430 \r
1431     MOV     ES:[DI], AH         ; set pixel\r
1432     LOOPjz  CX, @DL_STLExit     ; Delta_Y--, Exit if done\r
1433 \r
1434     ADD     SI, BX              ; add numerator to accumulator\r
1435     JNC     @DL_STLnc3          ; No carry, just move down!\r
1436 \r
1437     DEC     DI                  ; Move Left one addr\r
1438     ROR     AL, 1               ; Move Left one plane, back on 0 1 2\r
1439     CMP     AL, 87h             ; Wrap?, if AL <88 then Carry set\r
1440     ADC     DI, 0               ; Adjust Address: DI = DI + Carry\r
1441     OUT     DX, AL              ; Set up New Bit Plane mask\r
1442 \r
1443 @DL_STLnc3:\r
1444     ADD     DI, BP              ; advance to next line.\r
1445     JMP     s @DL_STLLoop       ; Loop until done\r
1446 \r
1447 @DL_STLExit:\r
1448     JMP     @DL_EXIT2           ; and exit\r
1449 \r
1450     ; Draw a line that goes to the Right...\r
1451 \r
1452 @DL_DrawRight:\r
1453     CMP     CX, DX              ; is Delta_X < Delta_Y?\r
1454     JB      @DL_SteepRight      ; yes, so go do steep line\r
1455                                 ; (Delta_Y iterations)\r
1456 \r
1457     ; Draw a Shallow line to the Right in Mode X\r
1458 \r
1459 @DL_ShallowRight:\r
1460     mov ax,0                  ; zero low word of Delta_Y * 10000h\r
1461     SUB     AX, DX              ; DX:AX <- DX * 0FFFFh\r
1462     SBB     DX, 0               ; include carry\r
1463     DIV     CX                  ; divide by Delta_X\r
1464 \r
1465     MOV     SI, BX              ; SI = Accumulator\r
1466     MOV     BX, AX              ; BX = Add Fraction\r
1467     POP     AX                  ; Get Color, Bit mask\r
1468     MOV     DX, SC_Data         ; Sequence controller data register\r
1469     INC     CX                  ; Inc Delta_X so we can unroll loop\r
1470 \r
1471     ; Loop (x2) to Draw Pixels, Move Right, and Maybe Down...\r
1472 \r
1473 @DL_SLRLoop:\r
1474     MOV     ES:[DI], AH         ; set first pixel, mask is set up\r
1475     LOOPjz  CX, @DL_SLRExit     ; Delta_X--, Exit if done..\r
1476 \r
1477     ADD     SI, BX              ; add numerator to accumulator\r
1478     JNC     @DL_SLR2nc          ; don't move down if carry not set\r
1479 \r
1480     ADD     DI, BP              ; Move Down one line...\r
1481 \r
1482 @DL_SLR2nc:                     ; Now move right a pixel...\r
1483     ROL     AL, 1               ; Move Right one addr if Plane = 0\r
1484     CMP     AL, 12h             ; Wrap? if AL >12 then Carry not set\r
1485     ADC     DI, 0               ; Adjust Address: DI = DI + Carry\r
1486     OUT     DX, AL              ; Set up New Bit Plane mask\r
1487 \r
1488     MOV     ES:[DI], AH         ; set pixel\r
1489     LOOPjz  CX, @DL_SLRExit     ; Delta_X--, Exit if done..\r
1490 \r
1491     ADD     SI, BX              ; add numerator to accumulator\r
1492     JNC     @DL_SLR3nc          ; don't move down if carry not set\r
1493 \r
1494     ADD     DI, BP              ; Move Down one line...\r
1495 \r
1496 @DL_SLR3nc:\r
1497     ROL     AL, 1               ; Move Right one addr if Plane = 0\r
1498     CMP     AL, 12h             ; Wrap? if AL >12 then Carry not set\r
1499     ADC     DI, 0               ; Adjust Address: DI = DI + Carry\r
1500     OUT     DX, AL              ; Set up New Bit Plane mask\r
1501     JMP     s @DL_SLRLoop       ; loop till done\r
1502 \r
1503 @DL_SLRExit:\r
1504     JMP     @DL_EXIT2           ; and exit\r
1505 \r
1506     ; Draw a Steep line to the Right in Mode X\r
1507 \r
1508 @DL_SteepRight:\r
1509     mov ax,0                  ; zero low word of Delta_Y * 10000h\r
1510     XCHG    DX, CX              ; Delta_Y switched with Delta_X\r
1511     DIV     CX                  ; divide by Delta_Y\r
1512 \r
1513     MOV     SI, BX              ; SI = Accumulator\r
1514     MOV     BX, AX              ; BX = Add Fraction\r
1515     POP     AX                  ; Get Color, Bit mask\r
1516     MOV     DX, SC_Data         ; Sequence controller data register\r
1517     INC     CX                  ; Inc Delta_Y so we can unroll loop\r
1518 \r
1519     ; Loop (x2) to Draw Pixels, Move Down, and Maybe Right\r
1520 \r
1521 @STRLoop:\r
1522     MOV     ES:[DI], AH         ; set first pixel, mask is set up\r
1523     LOOPjz  CX, @DL_EXIT2       ; Delta_Y--, Exit if Done\r
1524 \r
1525     ADD     SI, BX              ; add numerator to accumulator\r
1526     JNC     @STRnc2             ; if no carry then just go down...\r
1527 \r
1528     ROL     AL, 1               ; Move Right one addr if Plane = 0\r
1529     CMP     AL, 12h             ; Wrap? if AL >12 then Carry not set\r
1530     ADC     DI, 0               ; Adjust Address: DI = DI + Carry\r
1531     OUT     DX, AL              ; Set up New Bit Plane mask\r
1532 \r
1533 @STRnc2:\r
1534     ADD     DI, BP              ; advance to next line.\r
1535 \r
1536     MOV     ES:[DI], AH         ; set pixel\r
1537     LOOPjz  CX, @DL_EXIT2       ; Delta_Y--, Exit if Done\r
1538 \r
1539     ADD     SI, BX              ; add numerator to accumulator\r
1540     JNC     @STRnc3             ; if no carry then just go down...\r
1541 \r
1542     ROL     AL, 1               ; Move Right one addr if Plane = 0\r
1543     CMP     AL, 12h             ; Wrap? if AL >12 then Carry not set\r
1544     ADC     DI, 0               ; Adjust Address: DI = DI + Carry\r
1545     OUT     DX, AL              ; Set up New Bit Plane mask\r
1546 \r
1547 @STRnc3:\r
1548     ADD     DI, BP              ; advance to next line.\r
1549     JMP     s @STRLoop          ; loop till done\r
1550 \r
1551 @DL_EXIT2:\r
1552     ;POPx    DI, SI, BP          ; Restore Saved Registers\r
1553     pop di\r
1554         pop     si\r
1555         pop     bp\r
1556     RET     10                  ; Exit and Clean up Stack\r
1557 \r
1558 DRAW_LINE        ENDP\r
1559 \r
1560 \r
1561     ; ===== DAC COLOR REGISTER ROUTINES =====\r
1562 \r
1563 ;=================================================\r
1564 ;SET_DAC_REGISTER (Register%, Red%, Green%, Blue%)\r
1565 ;=================================================\r
1566 ;\r
1567 ; Sets a single (RGB) Vga Palette Register\r
1568 ;\r
1569 ; ENTRY: Register = The DAC # to modify (0-255)\r
1570 ;        Red      = The new Red Intensity (0-63)\r
1571 ;        Green    = The new Green Intensity (0-63)\r
1572 ;        Blue     = The new Blue Intensity (0-63)\r
1573 ;\r
1574 ; EXIT:  No meaningful values returned\r
1575 ;\r
1576 \r
1577 SDR_STACK   STRUC\r
1578                     DW  ?   ; BP\r
1579                     DD  ?   ; Caller\r
1580     SDR_Blue        DB  ?,? ; Blue Data Value\r
1581     SDR_Green       DB  ?,? ; Green Data Value\r
1582     SDR_Red         DB  ?,? ; Red Data Value\r
1583     SDR_Register    DB  ?,? ; Palette Register #\r
1584 SDR_STACK   ENDS\r
1585 \r
1586     PUBLIC  SET_DAC_REGISTER\r
1587 \r
1588 SET_DAC_REGISTER    PROC    FAR\r
1589 \r
1590     PUSH    BP                  ; Save BP\r
1591     MOV     BP, SP              ; Set up Stack Frame\r
1592 \r
1593     ; Select which DAC Register to modify\r
1594 \r
1595     OUT_8   DAC_WRITE_ADDR, [BP].SDR_Register\r
1596 \r
1597     MOV     DX, PEL_DATA_REG    ; Dac Data Register\r
1598     OUT_8   DX, [BP].SDR_Red    ; Set Red Intensity\r
1599     OUT_8   DX, [BP].SDR_Green  ; Set Green Intensity\r
1600     OUT_8   DX, [BP].SDR_Blue   ; Set Blue Intensity\r
1601 \r
1602     POP     BP                  ; Restore Registers\r
1603     RET     8                   ; Exit & Clean Up Stack\r
1604 \r
1605 SET_DAC_REGISTER    ENDP\r
1606 \r
1607 ;====================================================\r
1608 ;GET_DAC_REGISTER (Register%, &Red%, &Green%, &Blue%)\r
1609 ;====================================================\r
1610 ;\r
1611 ; Reads the RGB Values of a single Vga Palette Register\r
1612 ;\r
1613 ; ENTRY: Register = The DAC # to read (0-255)\r
1614 ;        Red      = Offset to Red Variable in DS\r
1615 ;        Green    = Offset to Green Variable in DS\r
1616 ;        Blue     = Offset to Blue Variable in DS\r
1617 ;\r
1618 ; EXIT:  The values of the integer variables Red,\r
1619 ;        Green, and Blue are set to the values\r
1620 ;        taken from the specified DAC register.\r
1621 ;\r
1622 \r
1623 GDR_STACK   STRUC\r
1624                     DW  ?   ; BP\r
1625                     DD  ?   ; Caller\r
1626     GDR_Blue        DW  ?   ; Addr of Blue Data Value in DS\r
1627     GDR_Green       DW  ?   ; Addr of Green Data Value in DS\r
1628     GDR_Red         DW  ?   ; Addr of Red Data Value in DS\r
1629     GDR_Register    DB  ?,? ; Palette Register #\r
1630 GDR_STACK   ENDS\r
1631 \r
1632     PUBLIC  GET_DAC_REGISTER\r
1633 \r
1634 GET_DAC_REGISTER    PROC    FAR\r
1635 \r
1636     PUSH    BP                  ; Save BP\r
1637     MOV     BP, SP              ; Set up Stack Frame\r
1638 \r
1639     ; Select which DAC Register to read in\r
1640 \r
1641     OUT_8   DAC_READ_ADDR, [BP].GDR_Register\r
1642 \r
1643     MOV     DX, PEL_DATA_REG    ; Dac Data Register\r
1644     mov ax,0                  ; Clear AX\r
1645 \r
1646     IN      AL, DX              ; Read Red Value\r
1647     MOV     BX, [BP].GDR_Red    ; Get Address of Red%\r
1648     MOV     [BX], AX            ; *Red% = AX\r
1649 \r
1650     IN      AL, DX              ; Read Green Value\r
1651     MOV     BX, [BP].GDR_Green  ; Get Address of Green%\r
1652     MOV     [BX], AX            ; *Green% = AX\r
1653 \r
1654     IN      AL, DX              ; Read Blue Value\r
1655     MOV     BX, [BP].GDR_Blue   ; Get Address of Blue%\r
1656     MOV     [BX], AX            ; *Blue% = AX\r
1657 \r
1658     POP     BP                  ; Restore Registers\r
1659     RET     8                   ; Exit & Clean Up Stack\r
1660 \r
1661 GET_DAC_REGISTER    ENDP\r
1662 \r
1663 \r
1664 ;===========================================================\r
1665 ;LOAD_DAC_REGISTERS (SEG PalData, StartReg%, EndReg%, Sync%)\r
1666 ;===========================================================\r
1667 ;\r
1668 ; Sets a Block of Vga Palette Registers\r
1669 ;\r
1670 ; ENTRY: PalData  = Far Pointer to Block of palette data\r
1671 ;        StartReg = First Register # in range to set (0-255)\r
1672 ;        EndReg   = Last Register # in Range to set (0-255)\r
1673 ;        Sync     = Wait for Vertical Retrace Flag (Boolean)\r
1674 ;\r
1675 ; EXIT:  No meaningful values returned\r
1676 ;\r
1677 ; NOTES: PalData is a linear array of 3 byte Palette values\r
1678 ;        in the order: Red  (0-63), Green (0-63), Blue (0-63)\r
1679 ;\r
1680 \r
1681 LDR_STACK   STRUC\r
1682                     DW  ?x1 ; BP, DS, SI\r
1683                     DW  ?x1 ; BP, DS, SI\r
1684                     DW  ?x1 ; BP, DS, SI\r
1685                     DD  ?   ; Caller\r
1686     LDR_Sync        DW  ?   ; Vertical Sync Flag\r
1687     LDR_EndReg      DB  ?,? ; Last Register #\r
1688     LDR_StartReg    DB  ?,? ; First Register #\r
1689     LDR_PalData     DD  ?   ; Far Ptr to Palette Data\r
1690 LDR_STACK   ENDS\r
1691 \r
1692     PUBLIC  LOAD_DAC_REGISTERS\r
1693 \r
1694 LOAD_DAC_REGISTERS  PROC    FAR\r
1695 \r
1696     ;PUSHx   BP, DS, SI          ; Save Registers\r
1697     push        bp\r
1698         push    ds\r
1699         push    si\r
1700     mov     BP, SP              ; Set up Stack Frame\r
1701 \r
1702     mov     AX, [BP].LDR_Sync   ; Get Vertical Sync Flag\r
1703     or      AX, AX              ; is Sync Flag = 0?\r
1704     jz      @LDR_Load           ; if so, skip call\r
1705 \r
1706     call    f SYNC_DISPLAY      ; wait for vsync\r
1707 \r
1708     ; Determine register #'s, size to copy, etc\r
1709 \r
1710 @LDR_Load:\r
1711 \r
1712     lds     SI, [BP].LDR_PalData    ; DS:SI -> Palette Data\r
1713     mov     DX, DAC_WRITE_ADDR      ; DAC register # selector\r
1714 \r
1715     mov ax,0, BX                  ; Clear for byte loads\r
1716     mov     AL, [BP].LDR_StartReg   ; Get Start Register\r
1717     mov     BL, [BP].LDR_EndReg     ; Get End Register\r
1718 \r
1719     sub     BX, AX              ; BX = # of DAC registers -1\r
1720     inc     BX                  ; BX = # of DAC registers\r
1721     mov     CX, BX              ; CX = # of DAC registers\r
1722     add     CX, BX              ; CX =  "   " * 2\r
1723     add     CX, BX              ; CX =  "   " * 3\r
1724     cld                         ; Block OUTs forward\r
1725     out     DX, AL              ; set up correct register #\r
1726 \r
1727     ; Load a block of DAC Registers\r
1728 \r
1729     mov     DX, PEL_DATA_REG    ; Dac Data Register\r
1730 \r
1731     ;rep     outsb               ; block set DAC registers\r
1732 \r
1733     ;POPx    SI, DS, BP          ; Restore Registers\r
1734         pop     si\r
1735         pop     ds\r
1736         pop     bp\r
1737     ret     10                  ; Exit & Clean Up Stack\r
1738 \r
1739 LOAD_DAC_REGISTERS  ENDP\r
1740 \r
1741 \r
1742 ;====================================================\r
1743 ;READ_DAC_REGISTERS (SEG PalData, StartReg%, EndReg%)\r
1744 ;====================================================\r
1745 ;\r
1746 ; Reads a Block of Vga Palette Registers\r
1747 ;\r
1748 ; ENTRY: PalData  = Far Pointer to block to store palette data\r
1749 ;        StartReg = First Register # in range to read (0-255)\r
1750 ;        EndReg   = Last Register # in Range to read (0-255)\r
1751 ;\r
1752 ; EXIT:  No meaningful values returned\r
1753 ;\r
1754 ; NOTES: PalData is a linear array of 3 byte Palette values\r
1755 ;        in the order: Red  (0-63), Green (0-63), Blue (0-63)\r
1756 ;\r
1757 \r
1758 RDR_STACK   STRUC\r
1759                     DW  ?x1 ; BP, ES, DI\r
1760                     DW  ?x1 ; BP, ES, DI\r
1761                     DW  ?x1 ; BP, ES, DI\r
1762                     DD  ?   ; Caller\r
1763     RDR_EndReg      DB  ?,? ; Last Register #\r
1764     RDR_StartReg    DB  ?,? ; First Register #\r
1765     RDR_PalData     DD  ?   ; Far Ptr to Palette Data\r
1766 RDR_STACK   ENDS\r
1767 \r
1768     PUBLIC  READ_DAC_REGISTERS\r
1769 \r
1770 READ_DAC_REGISTERS  PROC    FAR\r
1771 \r
1772     ;PUSHx   BP, ES, DI          ; Save Registers\r
1773     push        bp\r
1774         push    es\r
1775         push    di\r
1776     mov     BP, SP              ; Set up Stack Frame\r
1777 \r
1778     ; Determine register #'s, size to copy, etc\r
1779 \r
1780     les     DI, [BP].RDR_PalData    ; ES:DI -> Palette Buffer\r
1781     mov     DX, DAC_READ_ADDR       ; DAC register # selector\r
1782 \r
1783     mov ax,0, BX                  ; Clear for byte loads\r
1784     mov     AL, [BP].RDR_StartReg   ; Get Start Register\r
1785     mov     BL, [BP].RDR_EndReg     ; Get End Register\r
1786 \r
1787     sub     BX, AX              ; BX = # of DAC registers -1\r
1788     inc     BX                  ; BX = # of DAC registers\r
1789     mov     CX, BX              ; CX = # of DAC registers\r
1790     add     CX, BX              ; CX =  "   " * 2\r
1791     add     CX, BX              ; CX =  "   " * 3\r
1792     cld                         ; Block INs forward\r
1793 \r
1794     ; Read a block of DAC Registers\r
1795 \r
1796     out     DX, AL              ; set up correct register #\r
1797     mov     DX, PEL_DATA_REG    ; Dac Data Register\r
1798 \r
1799     ;rep     insb                ; block read DAC registers\r
1800 \r
1801     ;POPx    DI, ES, BP          ; Restore Registers\r
1802     pop di\r
1803         pop     es\r
1804         pop     bp\r
1805     ret     8                   ; Exit & Clean Up Stack\r
1806 \r
1807 READ_DAC_REGISTERS  ENDP\r
1808 \r
1809 \r
1810     ; ===== PAGE FLIPPING AND SCROLLING ROUTINES =====\r
1811 \r
1812 ;=========================\r
1813 ;SET_ACTIVE_PAGE (PageNo%)\r
1814 ;=========================\r
1815 ;\r
1816 ; Sets the active display Page to be used for future drawing\r
1817 ;\r
1818 ; ENTRY: PageNo = Display Page to make active\r
1819 ;        (values: 0 to Number of Pages - 1)\r
1820 ;\r
1821 ; EXIT:  No meaningful values returned\r
1822 ;\r
1823 \r
1824 SAP_STACK   STRUC\r
1825                 DW  ?   ; BP\r
1826                 DD  ?   ; Caller\r
1827     SAP_Page    DW  ?   ; Page # for Drawing\r
1828 SAP_STACK   ENDS\r
1829 \r
1830     PUBLIC  SET_ACTIVE_PAGE\r
1831 \r
1832 SET_ACTIVE_PAGE PROC    FAR\r
1833 \r
1834     PUSH    BP                  ; Preserve Registers\r
1835     MOV     BP, SP              ; Set up Stack Frame\r
1836 \r
1837     MOV     BX, [BP].SAP_Page   ; Get Desired Page #\r
1838     CMP     BX, LAST_PAGE       ; Is Page # Valid?\r
1839     JAE     @SAP_Exit           ; IF Not, Do Nothing\r
1840 \r
1841     MOV     ACTIVE_PAGE, BX     ; Set Active Page #\r
1842 \r
1843     SHL     BX, 1               ; Scale Page # to Word\r
1844     MOV     AX, PAGE_ADDR[BX]   ; Get offset to Page\r
1845 \r
1846     MOV     CURRENT_PAGE, AX    ; And set for future LES's\r
1847 \r
1848 @SAP_Exit:\r
1849     POP     BP                  ; Restore Registers\r
1850     RET     2                   ; Exit and Clean up Stack\r
1851 \r
1852 SET_ACTIVE_PAGE ENDP\r
1853 \r
1854 \r
1855 ;================\r
1856 ;GET_ACTIVE_PAGE%\r
1857 ;================\r
1858 ;\r
1859 ; Returns the Video Page # currently used for Drawing\r
1860 ;\r
1861 ; ENTRY: No Parameters are passed\r
1862 ;\r
1863 ; EXIT:  AX = Current Video Page used for Drawing\r
1864 ;\r
1865 \r
1866     PUBLIC  GET_ACTIVE_PAGE\r
1867 \r
1868 GET_ACTIVE_PAGE PROC    FAR\r
1869 \r
1870     MOV     AX, ACTIVE_PAGE     ; Get Active Page #\r
1871     RET                         ; Exit and Clean up Stack\r
1872 \r
1873 GET_ACTIVE_PAGE ENDP\r
1874 \r
1875 \r
1876 ;===============================\r
1877 ;SET_DISPLAY_PAGE (DisplayPage%)\r
1878 ;===============================\r
1879 ;\r
1880 ; Sets the currently visible display page.\r
1881 ; When called this routine syncronizes the display\r
1882 ; to the vertical blank.\r
1883 ;\r
1884 ; ENTRY: PageNo = Display Page to show on the screen\r
1885 ;        (values: 0 to Number of Pages - 1)\r
1886 ;\r
1887 ; EXIT:  No meaningful values returned\r
1888 ;\r
1889 \r
1890 SDP_STACK   STRUC\r
1891                 DW  ?       ; BP\r
1892                 DD  ?       ; Caller\r
1893     SDP_Page    DW  ?       ; Page # to Display...\r
1894 SDP_STACK   ENDS\r
1895 \r
1896     PUBLIC  SET_DISPLAY_PAGE\r
1897 \r
1898 SET_DISPLAY_PAGE    PROC    FAR\r
1899 \r
1900     PUSH    BP                  ; Preserve Registers\r
1901     MOV     BP, SP              ; Set up Stack Frame\r
1902 \r
1903     MOV     BX, [BP].SDP_Page   ; Get Desired Page #\r
1904     CMP     BX, LAST_PAGE       ; Is Page # Valid?\r
1905     JAE     @SDP_Exit           ; IF Not, Do Nothing\r
1906 \r
1907     MOV     DISPLAY_PAGE, BX    ; Set Display Page #\r
1908 \r
1909     SHL     BX, 1               ; Scale Page # to Word\r
1910     MOV     CX, PAGE_ADDR[BX]   ; Get offset in memory to Page\r
1911     ADD     CX, CURRENT_MOFFSET ; Adjust for any scrolling\r
1912 \r
1913     ; Wait if we are currently in a Vertical Retrace\r
1914 \r
1915     MOV     DX, INPUT_1         ; Input Status #1 Register\r
1916 \r
1917 @DP_WAIT0:\r
1918     IN      AL, DX              ; Get VGA status\r
1919     AND     AL, VERT_RETRACE    ; In Display mode yet?\r
1920     JNZ     @DP_WAIT0           ; If Not, wait for it\r
1921 \r
1922     ; Set the Start Display Address to the new page\r
1923 \r
1924     MOV     DX, CRTC_Index      ; We Change the VGA Sequencer\r
1925 \r
1926     MOV     AL, START_DISP_LO   ; Display Start Low Register\r
1927     MOV     AH, CL              ; Low 8 Bits of Start Addr\r
1928     OUT     DX, AX              ; Set Display Addr Low\r
1929 \r
1930     MOV     AL, START_DISP_HI   ; Display Start High Register\r
1931     MOV     AH, CH              ; High 8 Bits of Start Addr\r
1932     OUT     DX, AX              ; Set Display Addr High\r
1933 \r
1934     ; Wait for a Vertical Retrace to smooth out things\r
1935 \r
1936     MOV     DX, INPUT_1         ; Input Status #1 Register\r
1937 \r
1938 @DP_WAIT1:\r
1939     IN      AL, DX              ; Get VGA status\r
1940     AND     AL, VERT_RETRACE    ; Vertical Retrace Start?\r
1941     JZ      @DP_WAIT1           ; If Not, wait for it\r
1942 \r
1943     ; Now Set Display Starting Address\r
1944 \r
1945 \r
1946 @SDP_Exit:\r
1947     POP     BP                  ; Restore Registers\r
1948     RET     2                   ; Exit and Clean up Stack\r
1949 \r
1950 SET_DISPLAY_PAGE    ENDP\r
1951 \r
1952 \r
1953 ;=================\r
1954 ;GET_DISPLAY_PAGE%\r
1955 ;=================\r
1956 ;\r
1957 ; Returns the Video Page # currently displayed\r
1958 ;\r
1959 ; ENTRY: No Parameters are passed\r
1960 ;\r
1961 ; EXIT:  AX = Current Video Page being displayed\r
1962 ;\r
1963 \r
1964     PUBLIC  GET_DISPLAY_PAGE\r
1965 \r
1966 GET_DISPLAY_PAGE    PROC    FAR\r
1967 \r
1968     MOV     AX, DISPLAY_PAGE    ; Get Display Page #\r
1969     RET                         ; Exit & Clean Up Stack\r
1970 \r
1971 GET_DISPLAY_PAGE    ENDP\r
1972 \r
1973 \r
1974 ;=======================================\r
1975 ;SET_WINDOW (DisplayPage%, Xpos%, Ypos%)\r
1976 ;=======================================\r
1977 ;\r
1978 ; Since a Logical Screen can be larger than the Physical\r
1979 ; Screen, Scrolling is possible.  This routine sets the\r
1980 ; Upper Left Corner of the Screen to the specified Pixel.\r
1981 ; Also Sets the Display page to simplify combined page\r
1982 ; flipping and scrolling.  When called this routine\r
1983 ; syncronizes the display to the vertical blank.\r
1984 ;\r
1985 ; ENTRY: DisplayPage = Display Page to show on the screen\r
1986 ;        Xpos        = # of pixels to shift screen right\r
1987 ;        Ypos        = # of lines to shift screen down\r
1988 ;\r
1989 ; EXIT:  No meaningful values returned\r
1990 ;\r
1991 \r
1992 SW_STACK    STRUC\r
1993                 DW  ?   ; BP\r
1994                 DD  ?   ; Caller\r
1995     SW_Ypos     DW  ?   ; Y pos of UL Screen Corner\r
1996     SW_Xpos     DW  ?   ; X pos of UL Screen Corner\r
1997     SW_Page     DW  ?   ; (new) Display Page\r
1998 SW_STACK    ENDS\r
1999 \r
2000         PUBLIC SET_WINDOW\r
2001 \r
2002 SET_WINDOW  PROC    FAR\r
2003 \r
2004     PUSH    BP                  ; Preserve Registers\r
2005     MOV     BP, SP              ; Set up Stack Frame\r
2006 \r
2007     ; Check if our Scroll Offsets are Valid\r
2008 \r
2009     MOV     BX, [BP].SW_Page    ; Get Desired Page #\r
2010     CMP     BX, LAST_PAGE       ; Is Page # Valid?\r
2011     JAE     @SW_Exit            ; IF Not, Do Nothing\r
2012 \r
2013     MOV     AX, [BP].SW_Ypos    ; Get Desired Y Offset\r
2014     CMP     AX, MAX_YOFFSET     ; Is it Within Limits?\r
2015     JA      @SW_Exit            ; if not, exit\r
2016 \r
2017     MOV     CX, [BP].SW_Xpos    ; Get Desired X Offset\r
2018     CMP     CX, MAX_XOFFSET     ; Is it Within Limits?\r
2019     JA      @SW_Exit            ; if not, exit\r
2020 \r
2021     ; Compute proper Display start address to use\r
2022 \r
2023     MUL     SCREEN_WIDTH        ; AX = YOffset * Line Width\r
2024     SHR     CX, 1               ; CX / 4 = Bytes into Line\r
2025     SHR     CX, 1               ; CX / 4 = Bytes into Line\r
2026     ADD     AX, CX              ; AX = Offset of Upper Left Pixel\r
2027 \r
2028     MOV     CURRENT_MOFFSET, AX ; Save Offset Info\r
2029 \r
2030     MOV     DISPLAY_PAGE, BX    ; Set Current Page #\r
2031     SHL     BX, 1               ; Scale Page # to Word\r
2032     ADD     AX, PAGE_ADDR[BX]   ; Get offset in VGA to Page\r
2033     MOV     BX, AX              ; BX = Desired Display Start\r
2034 \r
2035     MOV     DX, INPUT_1         ; Input Status #1 Register\r
2036 \r
2037     ; Wait if we are currently in a Vertical Retrace\r
2038 \r
2039 @SW_WAIT0:\r
2040     IN      AL, DX              ; Get VGA status\r
2041     AND     AL, VERT_RETRACE    ; In Display mode yet?\r
2042     JNZ     @SW_WAIT0           ; If Not, wait for it\r
2043 \r
2044     ; Set the Start Display Address to the new window\r
2045 \r
2046     MOV     DX, CRTC_Index      ; We Change the VGA Sequencer\r
2047     MOV     AL, START_DISP_LO   ; Display Start Low Register\r
2048     MOV     AH, BL              ; Low 8 Bits of Start Addr\r
2049     OUT     DX, AX              ; Set Display Addr Low\r
2050 \r
2051     MOV     AL, START_DISP_HI   ; Display Start High Register\r
2052     MOV     AH, BH              ; High 8 Bits of Start Addr\r
2053     OUT     DX, AX              ; Set Display Addr High\r
2054 \r
2055     ; Wait for a Vertical Retrace to smooth out things\r
2056 \r
2057     MOV     DX, INPUT_1         ; Input Status #1 Register\r
2058 \r
2059 @SW_WAIT1:\r
2060     IN      AL, DX              ; Get VGA status\r
2061     AND     AL, VERT_RETRACE    ; Vertical Retrace Start?\r
2062     JZ      @SW_WAIT1           ; If Not, wait for it\r
2063 \r
2064     ; Now Set the Horizontal Pixel Pan values\r
2065 \r
2066     OUT_8   ATTRIB_Ctrl, PIXEL_PAN_REG  ; Select Pixel Pan Register\r
2067 \r
2068     MOV     AX, [BP].SW_Xpos    ; Get Desired X Offset\r
2069     AND     AL, 03              ; Get # of Pixels to Pan (0-3)\r
2070     SHL     AL, 1               ; Shift for 256 Color Mode\r
2071     OUT     DX, AL              ; Fine tune the display!\r
2072 \r
2073 @SW_Exit:\r
2074     POP     BP                  ; Restore Saved Registers\r
2075     RET     6                   ; Exit and Clean up Stack\r
2076 \r
2077 SET_WINDOW        ENDP\r
2078 \r
2079 \r
2080 ;=============\r
2081 ;GET_X_OFFSET%\r
2082 ;=============\r
2083 ;\r
2084 ; Returns the X coordinate of the Pixel currently display\r
2085 ; in the upper left corner of the display\r
2086 ;\r
2087 ; ENTRY: No Parameters are passed\r
2088 ;\r
2089 ; EXIT:  AX = Current Horizontal Scroll Offset\r
2090 ;\r
2091 \r
2092     PUBLIC  GET_X_OFFSET\r
2093 \r
2094 GET_X_OFFSET    PROC    FAR\r
2095 \r
2096     MOV     AX, CURRENT_XOFFSET ; Get current horz offset\r
2097     RET                         ; Exit & Clean Up Stack\r
2098 \r
2099 GET_X_OFFSET    ENDP\r
2100 \r
2101 \r
2102 ;=============\r
2103 ;GET_Y_OFFSET%\r
2104 ;=============\r
2105 ;\r
2106 ; Returns the Y coordinate of the Pixel currently display\r
2107 ; in the upper left corner of the display\r
2108 ;\r
2109 ; ENTRY: No Parameters are passed\r
2110 ;\r
2111 ; EXIT:  AX = Current Vertical Scroll Offset\r
2112 ;\r
2113 \r
2114     PUBLIC  GET_Y_OFFSET\r
2115 \r
2116 GET_Y_OFFSET    PROC    FAR\r
2117 \r
2118     MOV     AX, CURRENT_YOFFSET ; Get current vertical offset\r
2119     RET                         ; Exit & Clean Up Stack\r
2120 \r
2121 GET_Y_OFFSET    ENDP\r
2122 \r
2123 \r
2124 ;============\r
2125 ;SYNC_DISPLAY\r
2126 ;============\r
2127 ;\r
2128 ; Pauses the computer until the next Vertical Retrace starts\r
2129 ;\r
2130 ; ENTRY: No Parameters are passed\r
2131 ;\r
2132 ; EXIT:  No meaningful values returned\r
2133 ;\r
2134 \r
2135     PUBLIC  SYNC_DISPLAY\r
2136 \r
2137 SYNC_DISPLAY    PROC    FAR\r
2138 \r
2139     MOV     DX, INPUT_1         ; Input Status #1 Register\r
2140 \r
2141     ; Wait for any current retrace to end\r
2142 \r
2143 @SD_WAIT0:\r
2144     IN      AL, DX              ; Get VGA status\r
2145     AND     AL, VERT_RETRACE    ; In Display mode yet?\r
2146     JNZ     @SD_WAIT0           ; If Not, wait for it\r
2147 \r
2148     ; Wait for the start of the next vertical retrace\r
2149 \r
2150 @SD_WAIT1:\r
2151     IN      AL, DX              ; Get VGA status\r
2152     AND     AL, VERT_RETRACE    ; Vertical Retrace Start?\r
2153     JZ      @SD_WAIT1           ; If Not, wait for it\r
2154 \r
2155     RET                         ; Exit & Clean Up Stack\r
2156 \r
2157 SYNC_DISPLAY    ENDP\r
2158 \r
2159 \r
2160     ; ===== TEXT DISPLAY ROUTINES =====\r
2161 \r
2162 ;==================================================\r
2163 ;GPRINTC (CharNum%, Xpos%, Ypos%, ColorF%, ColorB%)\r
2164 ;==================================================\r
2165 ;\r
2166 ; Draws an ASCII Text Character using the currently selected\r
2167 ; 8x8 font on the active display page.  It would be a simple\r
2168 ; exercise to make this routine process variable height fonts.\r
2169 ;\r
2170 ; ENTRY: CharNum = ASCII character # to draw\r
2171 ;        Xpos    = X position to draw Character at\r
2172 ;        Ypos    = Y position of to draw Character at\r
2173 ;        ColorF  = Color to draw text character in\r
2174 ;        ColorB  = Color to set background to\r
2175 ;\r
2176 ; EXIT:  No meaningful values returned\r
2177 ;\r
2178 \r
2179 GPC_STACK   STRUC\r
2180     GPC_Width   DW  ?   ; Screen Width-1\r
2181     GPC_Lines   DB  ?,? ; Scan lines to Decode\r
2182     GPC_T_SETS  DW  ?   ; Saved Charset Segment\r
2183     GPC_T_SETO  DW  ?   ; Saved Charset Offset\r
2184                 DW  ?x1 ; DI, SI, DS, BP\r
2185                 DW  ?x1 ; DS, DI, SI, BP\r
2186                 DW  ?x1 ; DS, DI, SI, BP\r
2187                 DW  ?x1 ; DS, DI, SI, BP\r
2188                 DD  ?   ; Caller\r
2189     GPC_ColorB  DB  ?,? ; Background Color\r
2190     GPC_ColorF  DB  ?,? ; Text Color\r
2191     GPC_Ypos    DW  ?   ; Y Position to Print at\r
2192     GPC_Xpos    DW  ?   ; X position to Print at\r
2193     GPC_Char    DB  ?,? ; Character to Print\r
2194 GPC_STACK   ENDS\r
2195 \r
2196         PUBLIC GPRINTC\r
2197 \r
2198 GPRINTC     PROC    FAR\r
2199 \r
2200     ;PUSHx   BP, DS, SI, DI      ; Preserve Important Registers\r
2201     push        bp\r
2202         push    ds\r
2203         push    si\r
2204         push    di\r
2205     SUB     SP, 8               ; Allocate WorkSpace on Stack\r
2206     MOV     BP, SP              ; Set up Stack Frame\r
2207 \r
2208     LES     DI, d CURRENT_PAGE  ; Point to Active VGA Page\r
2209 \r
2210     MOV     AX, SCREEN_WIDTH    ; Get Logical Line Width\r
2211     MOV     BX, AX              ; BX = Screen Width\r
2212     DEC     BX                  ;    = Screen Width-1\r
2213     MOV     [BP].GPC_Width, BX  ; Save for later use\r
2214 \r
2215     MUL     [BP].GPC_Ypos       ; Start of Line = Ypos * Width\r
2216     ADD     DI, AX              ; DI -> Start of Line Ypos\r
2217 \r
2218     MOV     AX, [BP].GPC_Xpos   ; Get Xpos of Character\r
2219     MOV     CX, AX              ; Save Copy of Xpos\r
2220     SHR     AX, 1               ; Bytes into Line = Xpos/4\r
2221     SHR     AX, 1               ; Bytes into Line = Xpos/4\r
2222     ADD     DI, AX              ; DI -> (Xpos, Ypos)\r
2223 \r
2224     ;Get Source ADDR of Character Bit Map  & Save\r
2225 \r
2226     MOV     AL, [BP].GPC_Char   ; Get Character #\r
2227     TEST    AL, 080h            ; Is Hi Bit Set?\r
2228     JZ      @GPC_LowChar        ; Nope, use low char set ptr\r
2229 \r
2230     AND     AL, 07Fh            ; Mask Out Hi Bit\r
2231     MOV     BX, CHARSET_HI      ; BX = Char Set Ptr:Offset\r
2232     MOV     DX, CHARSET_HI+2    ; DX = Char Set Ptr:Segment\r
2233     JMP     s @GPC_Set_Char     ; Go Setup Character Ptr\r
2234 \r
2235 @GPC_LowChar:\r
2236 \r
2237     MOV     BX, CHARSET_LOW     ; BX = Char Set Ptr:Offset\r
2238     MOV     DX, CHARSET_LOW+2   ; DX = Char Set Ptr:Segment\r
2239 \r
2240 @GPC_Set_Char:\r
2241     MOV     [BP].GPC_T_SETS, DX ; Save Segment on Stack\r
2242 \r
2243     MOV     AH, 0               ; Valid #'s are 0..127\r
2244     SHL     AX, 1               ; * 8 Bytes Per Bitmap\r
2245     SHL     AX, 1               ; * 8 Bytes Per Bitmap\r
2246     SHL     AX, 1               ; * 8 Bytes Per Bitmap\r
2247     ADD     BX, AX              ; BX = Offset of Selected char\r
2248     MOV     [BP].GPC_T_SETO, BX ; Save Offset on Stack\r
2249 \r
2250     AND     CX, PLANE_BITS      ; Get Plane #\r
2251     MOV     CH, ALL_PLANES      ; Get Initial Plane mask\r
2252     SHL     CH, CL              ; And shift into position\r
2253     AND     CH, ALL_PLANES      ; And mask to lower nibble\r
2254 \r
2255     MOV     AL, 04              ; 4-Plane # = # of initial\r
2256     SUB     AL, CL              ; shifts to align bit mask\r
2257     MOV     CL, AL              ; Shift Count for SHL\r
2258 \r
2259     ;Get segment of character map\r
2260 \r
2261     OUT_8   SC_Index, MAP_MASK  ; Setup Plane selections\r
2262     INC     DX                  ; DX -> SC_Data\r
2263 \r
2264     MOV     AL, 08              ; 8 Lines to Process\r
2265     MOV     [BP].GPC_Lines, AL  ; Save on Stack\r
2266 \r
2267     MOV     DS, [BP].GPC_T_SETS ; Point to character set\r
2268 \r
2269 @GPC_DECODE_CHAR_BYTE:\r
2270 \r
2271     MOV     SI, [BP].GPC_T_SETO ; Get DS:SI = String\r
2272 \r
2273     MOV     BH, [SI]            ; Get Bit Map\r
2274     INC     SI                  ; Point to Next Line\r
2275     MOV     [BP].GPC_T_SETO, SI ; And save new Pointer...\r
2276 \r
2277     mov ax,0                  ; Clear AX\r
2278 \r
2279     ;mov bl,0                      ; Clear BL\r
2280     mov bl,0\r
2281     ROL     BX, CL                  ; BL holds left edge bits\r
2282     MOV     SI, BX                  ; Use as Table Index\r
2283     AND     SI, CHAR_BITS           ; Get Low Bits\r
2284     MOV     AL, Char_Plane_Data[SI] ; Get Mask in AL\r
2285     JZ      @GPC_NO_LEFT1BITS       ; Skip if No Pixels to set\r
2286 \r
2287     MOV     AH, [BP].GPC_ColorF ; Get Foreground Color\r
2288     OUT     DX, AL              ; Set up Screen Mask\r
2289     MOV     ES:[DI], AH         ; Write Foreground color\r
2290 \r
2291 @GPC_NO_LEFT1BITS:\r
2292     XOR     AL, CH              ; Invert mask for Background\r
2293     JZ      @GPC_NO_LEFT0BITS   ; Hey, no need for this\r
2294 \r
2295     MOV     AH, [BP].GPC_ColorB ; Get background Color\r
2296     OUT     DX, AL              ; Set up Screen Mask\r
2297     MOV     ES:[DI], AH         ; Write Foreground color\r
2298 \r
2299     ;Now Do Middle/Last Band\r
2300 \r
2301 @GPC_NO_LEFT0BITS:\r
2302     INC     DI                  ; Point to next Byte\r
2303     ROL     BX, 1               ; Shift 4 bits\r
2304     ROL     BX, 1               ; Shift 4 bits\r
2305     ROL     BX, 1               ; Shift 4 bits\r
2306     ROL     BX, 1               ; Shift 4 bits\r
2307 \r
2308     MOV     SI, BX                  ; Make Lookup Pointer\r
2309     AND     SI, CHAR_BITS           ; Get Low Bits\r
2310     MOV     AL, Char_Plane_Data[SI] ; Get Mask in AL\r
2311     JZ      @GPC_NO_MIDDLE1BITS     ; Skip if no pixels to set\r
2312 \r
2313     MOV     AH, [BP].GPC_ColorF ; Get Foreground Color\r
2314     OUT     DX, AL              ; Set up Screen Mask\r
2315     MOV     ES:[DI], AH         ; Write Foreground color\r
2316 \r
2317 @GPC_NO_MIDDLE1BITS:\r
2318     XOR     AL, ALL_PLANES      ; Invert mask for Background\r
2319     JZ      @GPC_NO_MIDDLE0BITS ; Hey, no need for this\r
2320 \r
2321     MOV     AH, [BP].GPC_ColorB ; Get background Color\r
2322     OUT     DX, AL              ; Set up Screen Mask\r
2323     MOV     ES:[DI], AH         ; Write Foreground color\r
2324 \r
2325 @GPC_NO_MIDDLE0BITS:\r
2326     XOR     CH, ALL_PLANES      ; Invert Clip Mask\r
2327     CMP     CL, 4               ; Aligned by 4?\r
2328     JZ      @GPC_NEXT_LINE      ; If so, Exit now..\r
2329 \r
2330     INC     DI                  ; Point to next Byte\r
2331     ROL     BX, 1               ; Shift 4 bits\r
2332     ROL     BX, 1               ; Shift 4 bits\r
2333     ROL     BX, 1               ; Shift 4 bits\r
2334     ROL     BX, 1               ; Shift 4 bits\r
2335 \r
2336     MOV     SI, BX                  ; Make Lookup Pointer\r
2337     AND     SI, CHAR_BITS           ; Get Low Bits\r
2338     MOV     AL, Char_Plane_Data[SI] ; Get Mask in AL\r
2339     JZ      @GPC_NO_RIGHT1BITS      ; Skip if No Pixels to set\r
2340 \r
2341     MOV     AH, [BP].GPC_ColorF ; Get Foreground Color\r
2342     OUT     DX, AL              ; Set up Screen Mask\r
2343     MOV     ES:[DI], AH         ; Write Foreground color\r
2344 \r
2345 @GPC_NO_RIGHT1BITS:\r
2346 \r
2347     XOR     AL, CH              ; Invert mask for Background\r
2348     JZ      @GPC_NO_RIGHT0BITS  ; Hey, no need for this\r
2349 \r
2350     MOV     AH, [BP].GPC_ColorB ; Get background Color\r
2351     OUT     DX, AL              ; Set up Screen Mask\r
2352     MOV     ES:[DI], AH         ; Write Foreground color\r
2353 \r
2354 @GPC_NO_RIGHT0BITS:\r
2355     DEC     DI                  ; Adjust for Next Line Advance\r
2356 \r
2357 @GPC_NEXT_LINE:\r
2358     ADD     DI, [BP].GPC_Width  ; Point to Next Line\r
2359     XOR     CH, CHAR_BITS       ; Flip the Clip mask back\r
2360 \r
2361     DEC     [BP].GPC_Lines      ; Count Down Lines\r
2362     JZ      @GPC_EXIT           ; Ok... Done!\r
2363 \r
2364     JMP     @GPC_DECODE_CHAR_BYTE   ; Again! Hey!\r
2365 \r
2366 @GPC_EXIT:\r
2367     ADD     SP, 08              ; Deallocate stack workspace\r
2368     ;POPx    DI, SI, DS, BP      ; Restore Saved Registers\r
2369     pop di\r
2370         pop     si\r
2371         pop     ds\r
2372         pop     bp\r
2373     RET     10                  ; Exit and Clean up Stack\r
2374 \r
2375 GPRINTC  ENDP\r
2376 \r
2377 \r
2378 ;==========================================\r
2379 ;TGPRINTC (CharNum%, Xpos%, Ypos%, ColorF%)\r
2380 ;==========================================\r
2381 ;\r
2382 ; Transparently draws an ASCII Text Character using the\r
2383 ; currently selected 8x8 font on the active display page.\r
2384 ;\r
2385 ; ENTRY: CharNum = ASCII character # to draw\r
2386 ;        Xpos    = X position to draw Character at\r
2387 ;        Ypos    = Y position of to draw Character at\r
2388 ;        ColorF  = Color to draw text character in\r
2389 ;\r
2390 ; EXIT:  No meaningful values returned\r
2391 ;\r
2392 \r
2393 TGP_STACK   STRUC\r
2394     TGP_Width   DW  ?   ; Screen Width-1\r
2395     TGP_Lines   DB  ?,? ; Scan lines to Decode\r
2396     TGP_T_SETS  DW  ?   ; Saved Charset Segment\r
2397     TGP_T_SETO  DW  ?   ; Saved Charset Offset\r
2398                 DW  ?x1 ; DI, SI, DS, BP\r
2399                 DW  ?x1 ; DS, DI, SI, BP\r
2400                 DW  ?x1 ; DS, DI, SI, BP\r
2401                 DW  ?x1 ; DS, DI, SI, BP\r
2402                 DD  ?   ; Caller\r
2403     TGP_ColorF  DB  ?,? ; Text Color\r
2404     TGP_Ypos    DW  ?   ; Y Position to Print at\r
2405     TGP_Xpos    DW  ?   ; X position to Print at\r
2406     TGP_Char    DB  ?,? ; Character to Print\r
2407 TGP_STACK   ENDS\r
2408 \r
2409         PUBLIC TGPRINTC\r
2410 \r
2411 TGPRINTC    PROC    FAR\r
2412 \r
2413     ;PUSHx   BP, DS, SI, DI      ; Preserve Important Registers\r
2414     push        bp\r
2415         push    ds\r
2416         push    si\r
2417         push    di\r
2418     SUB     SP, 8               ; Allocate WorkSpace on Stack\r
2419     MOV     BP, SP              ; Set up Stack Frame\r
2420 \r
2421     LES     DI, d CURRENT_PAGE  ; Point to Active VGA Page\r
2422 \r
2423     MOV     AX, SCREEN_WIDTH    ; Get Logical Line Width\r
2424     MOV     BX, AX              ; BX = Screen Width\r
2425     DEC     BX                  ;    = Screen Width-1\r
2426     MOV     [BP].TGP_Width, BX  ; Save for later use\r
2427 \r
2428     MUL     [BP].TGP_Ypos       ; Start of Line = Ypos * Width\r
2429     ADD     DI, AX              ; DI -> Start of Line Ypos\r
2430 \r
2431     MOV     AX, [BP].TGP_Xpos   ; Get Xpos of Character\r
2432     MOV     CX, AX              ; Save Copy of Xpos\r
2433     SHR     AX, 1               ; Bytes into Line = Xpos/4\r
2434     SHR     AX, 1               ; Bytes into Line = Xpos/4\r
2435     ADD     DI, AX              ; DI -> (Xpos, Ypos)\r
2436 \r
2437     ;Get Source ADDR of Character Bit Map  & Save\r
2438 \r
2439     MOV     AL, [BP].TGP_Char   ; Get Character #\r
2440     TEST    AL, 080h            ; Is Hi Bit Set?\r
2441     JZ      @TGP_LowChar        ; Nope, use low char set ptr\r
2442 \r
2443     AND     AL, 07Fh            ; Mask Out Hi Bit\r
2444     MOV     BX, CHARSET_HI      ; BX = Char Set Ptr:Offset\r
2445     MOV     DX, CHARSET_HI+2    ; DX = Char Set Ptr:Segment\r
2446     JMP     s @TGP_Set_Char     ; Go Setup Character Ptr\r
2447 \r
2448 @TGP_LowChar:\r
2449 \r
2450     MOV     BX, CHARSET_LOW     ; BX = Char Set Ptr:Offset\r
2451     MOV     DX, CHARSET_LOW+2   ; DX = Char Set Ptr:Segment\r
2452 \r
2453 @TGP_Set_Char:\r
2454     MOV     [BP].TGP_T_SETS, DX ; Save Segment on Stack\r
2455 \r
2456     MOV     AH, 0               ; Valid #'s are 0..127\r
2457     SHL     AX, 1               ; * 8 Bytes Per Bitmap\r
2458     SHL     AX, 1               ; * 8 Bytes Per Bitmap\r
2459     SHL     AX, 1               ; * 8 Bytes Per Bitmap\r
2460     ADD     BX, AX              ; BX = Offset of Selected char\r
2461     MOV     [BP].TGP_T_SETO, BX ; Save Offset on Stack\r
2462 \r
2463     AND     CX, PLANE_BITS      ; Get Plane #\r
2464     MOV     CH, ALL_PLANES      ; Get Initial Plane mask\r
2465     SHL     CH, CL              ; And shift into position\r
2466     AND     CH, ALL_PLANES      ; And mask to lower nibble\r
2467 \r
2468     MOV     AL, 04              ; 4-Plane # = # of initial\r
2469     SUB     AL, CL              ; shifts to align bit mask\r
2470     MOV     CL, AL              ; Shift Count for SHL\r
2471 \r
2472     ;Get segment of character map\r
2473 \r
2474     OUT_8   SC_Index, MAP_MASK  ; Setup Plane selections\r
2475     INC     DX                  ; DX -> SC_Data\r
2476 \r
2477     MOV     AL, 08              ; 8 Lines to Process\r
2478     MOV     [BP].TGP_Lines, AL  ; Save on Stack\r
2479 \r
2480     MOV     DS, [BP].TGP_T_SETS ; Point to character set\r
2481 \r
2482 @TGP_DECODE_CHAR_BYTE:\r
2483 \r
2484     MOV     SI, [BP].TGP_T_SETO ; Get DS:SI = String\r
2485 \r
2486     MOV     BH, [SI]            ; Get Bit Map\r
2487     INC     SI                  ; Point to Next Line\r
2488     MOV     [BP].TGP_T_SETO, SI ; And save new Pointer...\r
2489 \r
2490     MOV     AH, [BP].TGP_ColorF ; Get Foreground Color\r
2491 \r
2492     mov bl,0                      ; Clear BL\r
2493     ROL     BX, CL                  ; BL holds left edge bits\r
2494     MOV     SI, BX                  ; Use as Table Index\r
2495     AND     SI, CHAR_BITS           ; Get Low Bits\r
2496     MOV     AL, Char_Plane_Data[SI] ; Get Mask in AL\r
2497     JZ      @TGP_NO_LEFT1BITS       ; Skip if No Pixels to set\r
2498 \r
2499     OUT     DX, AL              ; Set up Screen Mask\r
2500     MOV     ES:[DI], AH         ; Write Foreground color\r
2501 \r
2502     ;Now Do Middle/Last Band\r
2503 \r
2504 @TGP_NO_LEFT1BITS:\r
2505 \r
2506     INC     DI                  ; Point to next Byte\r
2507     ROL     BX, 1               ; Shift 4 bits\r
2508     ROL     BX, 1               ; Shift 4 bits\r
2509     ROL     BX, 1               ; Shift 4 bits\r
2510     ROL     BX, 1               ; Shift 4 bits\r
2511 \r
2512     MOV     SI, BX                  ; Make Lookup Pointer\r
2513     AND     SI, CHAR_BITS           ; Get Low Bits\r
2514     MOV     AL, Char_Plane_Data[SI] ; Get Mask in AL\r
2515     JZ      @TGP_NO_MIDDLE1BITS     ; Skip if no pixels to set\r
2516 \r
2517     OUT     DX, AL              ; Set up Screen Mask\r
2518     MOV     ES:[DI], AH         ; Write Foreground color\r
2519 \r
2520 @TGP_NO_MIDDLE1BITS:\r
2521     XOR     CH, ALL_PLANES      ; Invert Clip Mask\r
2522     CMP     CL, 4               ; Aligned by 4?\r
2523     JZ      @TGP_NEXT_LINE      ; If so, Exit now..\r
2524 \r
2525     INC     DI                  ; Point to next Byte\r
2526     ROL     BX, 1               ; Shift 4 bits\r
2527     ROL     BX, 1               ; Shift 4 bits\r
2528     ROL     BX, 1               ; Shift 4 bits\r
2529     ROL     BX, 1               ; Shift 4 bits\r
2530 \r
2531     MOV     SI, BX                  ; Make Lookup Pointer\r
2532     AND     SI, CHAR_BITS           ; Get Low Bits\r
2533     MOV     AL, Char_Plane_Data[SI] ; Get Mask in AL\r
2534     JZ      @TGP_NO_RIGHT1BITS      ; Skip if No Pixels to set\r
2535 \r
2536     OUT     DX, AL              ; Set up Screen Mask\r
2537     MOV     ES:[DI], AH         ; Write Foreground color\r
2538 \r
2539 @TGP_NO_RIGHT1BITS:\r
2540 \r
2541     DEC     DI                  ; Adjust for Next Line Advance\r
2542 \r
2543 @TGP_NEXT_LINE:\r
2544     ADD     DI, [BP].TGP_Width  ; Point to Next Line\r
2545     XOR     CH, CHAR_BITS       ; Flip the Clip mask back\r
2546 \r
2547     DEC     [BP].TGP_Lines      ; Count Down Lines\r
2548     JZ      @TGP_EXIT           ; Ok... Done!\r
2549 \r
2550     JMP     @TGP_DECODE_CHAR_BYTE   ; Again! Hey!\r
2551 \r
2552 @TGP_EXIT:\r
2553     ADD     SP, 08              ; Deallocate stack workspace\r
2554     ;POPx    DI, SI, DS, BP      ; Restore Saved Registers\r
2555     pop di\r
2556         pop     si\r
2557         pop     ds\r
2558         pop     bp\r
2559     RET     8                   ; Exit and Clean up Stack\r
2560 \r
2561 TGPRINTC    ENDP\r
2562 \r
2563 \r
2564 ;===============================================================\r
2565 ;PRINT_STR (SEG String, MaxLen%, Xpos%, Ypos%, ColorF%, ColorB%)\r
2566 ;===============================================================\r
2567 ;\r
2568 ; Routine to quickly Print a null terminated ASCII string on the\r
2569 ; active display page up to a maximum length.\r
2570 ;\r
2571 ; ENTRY: String  = Far Pointer to ASCII string to print\r
2572 ;        MaxLen  = # of characters to print if no null found\r
2573 ;        Xpos    = X position to draw Text at\r
2574 ;        Ypos    = Y position of to draw Text at\r
2575 ;        ColorF  = Color to draw text in\r
2576 ;        ColorB  = Color to set background to\r
2577 ;\r
2578 ; EXIT:  No meaningful values returned\r
2579 ;\r
2580 \r
2581 PS_STACK    STRUC\r
2582                 DW  ?x1 ; DI, SI, DS, BP\r
2583                 DW  ?x1 ; DI, SI, DS, BP\r
2584                 DW  ?x1 ; DS, DI, SI, BP\r
2585                 DW  ?x1 ; DS, DI, SI, BP\r
2586                 DD  ?   ; Caller\r
2587     PS_ColorB   DW  ?   ; Background Color\r
2588     PS_ColorF   DW  ?   ; Text Color\r
2589     PS_Ypos     DW  ?   ; Y Position to Print at\r
2590     PS_Xpos     DW  ?   ; X position to Print at\r
2591     PS_Len      DW  ?   ; Maximum Length of string to print\r
2592     PS_Text     DW  ?,? ; Far Ptr to Text String\r
2593 PS_STACK    ENDS\r
2594 \r
2595         PUBLIC  PRINT_STR\r
2596 \r
2597 PRINT_STR   PROC    FAR\r
2598 \r
2599     ;PUSHx   BP, DS, SI, DI      ; Preserve Important Registers\r
2600     push        bp\r
2601         push    ds\r
2602         push    si\r
2603         push    di\r
2604     MOV     BP, SP              ; Set up Stack Frame\r
2605 \r
2606 @PS_Print_It:\r
2607 \r
2608     MOV     CX, [BP].PS_Len     ; Get Remaining text Length\r
2609     JCXZ    @PS_Exit            ; Exit when out of text\r
2610 \r
2611     LES     DI, d [BP].PS_Text  ; ES:DI -> Current Char in Text\r
2612     MOV     AL, ES:[DI]         ; AL = Text Character\r
2613     AND     AX, 00FFh           ; Clear High Word\r
2614     JZ      @PS_Exit            ; Exit if null character\r
2615 \r
2616     DEC     [BP].PS_Len         ; Remaining Text length--\r
2617     INC     [BP].PS_Text        ; Point to Next text char\r
2618 \r
2619     ; Set up Call to GPRINTC\r
2620 \r
2621     PUSH    AX                  ; Set Character Parameter\r
2622     MOV     BX, [BP].PS_Xpos    ; Get Xpos\r
2623     PUSH    BX                  ; Set Xpos Parameter\r
2624     ADD     BX, 8               ; Advance 1 Char to Right\r
2625     MOV     [BP].PS_Xpos, BX    ; Save for next time through\r
2626 \r
2627     MOV     BX, [BP].PS_Ypos    ; Get Ypos\r
2628     PUSH    BX                  ; Set Ypos Parameter\r
2629 \r
2630     MOV     BX, [BP].PS_ColorF  ; Get Text Color\r
2631     PUSH    BX                  ; Set ColorF Parameter\r
2632 \r
2633     MOV     BX, [BP].PS_ColorB  ; Get Background Color\r
2634     PUSH    BX                  ; Set ColorB Parameter\r
2635 \r
2636     CALL    f GPRINTC           ; Print Character!\r
2637     JMP     s @PS_Print_It      ; Process next character\r
2638 \r
2639 @PS_Exit:\r
2640     ;POPx    DI, SI, DS, BP      ; Restore Saved Registers\r
2641     pop di\r
2642         pop     si\r
2643         pop     ds\r
2644         pop     bp\r
2645     RET     14                  ; Exit and Clean up Stack\r
2646 \r
2647 PRINT_STR  ENDP\r
2648 \r
2649 \r
2650 ;================================================================\r
2651 ;TPRINT_STR (SEG String, MaxLen%, Xpos%, Ypos%, ColorF%, ColorB%)\r
2652 ;================================================================\r
2653 ;\r
2654 ; Routine to quickly transparently Print a null terminated ASCII\r
2655 ; string on the active display page up to a maximum length.\r
2656 ;\r
2657 ; ENTRY: String  = Far Pointer to ASCII string to print\r
2658 ;        MaxLen  = # of characters to print if no null found\r
2659 ;        Xpos    = X position to draw Text at\r
2660 ;        Ypos    = Y position of to draw Text at\r
2661 ;        ColorF  = Color to draw text in\r
2662 ;\r
2663 ; EXIT:  No meaningful values returned\r
2664 ;\r
2665 \r
2666 TPS_STACK   STRUC\r
2667                 DW  ?x1 ; DI, SI, DS, BP\r
2668                 DW  ?x1 ; DI, SI, DS, BP\r
2669                 DW  ?x1 ; DS, DI, SI, BP\r
2670                 DW  ?x1 ; DS, DI, SI, BP\r
2671                 DD  ?   ; Caller\r
2672     TPS_ColorF  DW  ?   ; Text Color\r
2673     TPS_Ypos    DW  ?   ; Y Position to Print at\r
2674     TPS_Xpos    DW  ?   ; X position to Print at\r
2675     TPS_Len     DW  ?   ; Maximum Length of string to print\r
2676     TPS_Text    DW  ?,? ; Far Ptr to Text String\r
2677 TPS_STACK   ENDS\r
2678 \r
2679         PUBLIC  TPRINT_STR\r
2680 \r
2681 TPRINT_STR  PROC    FAR\r
2682 \r
2683     ;PUSHx   BP, DS, SI, DI      ; Preserve Important Registers\r
2684     push        bp\r
2685         push    ds\r
2686         push    si\r
2687         push    di\r
2688     MOV     BP, SP              ; Set up Stack Frame\r
2689 \r
2690 @TPS_Print_It:\r
2691 \r
2692     MOV     CX, [BP].TPS_Len    ; Get Remaining text Length\r
2693     JCXZ    @TPS_Exit           ; Exit when out of text\r
2694 \r
2695     LES     DI, d [BP].TPS_Text ; ES:DI -> Current Char in Text\r
2696     MOV     AL, ES:[DI]         ; AL = Text Character\r
2697     AND     AX, 00FFh           ; Clear High Word\r
2698     JZ      @TPS_Exit           ; Exit if null character\r
2699 \r
2700     DEC     [BP].TPS_Len        ; Remaining Text length--\r
2701     INC     [BP].TPS_Text       ; Point to Next text char\r
2702 \r
2703     ; Set up Call to TGPRINTC\r
2704 \r
2705     PUSH    AX                  ; Set Character Parameter\r
2706     MOV     BX, [BP].TPS_Xpos   ; Get Xpos\r
2707     PUSH    BX                  ; Set Xpos Parameter\r
2708     ADD     BX, 8               ; Advance 1 Char to Right\r
2709     MOV     [BP].TPS_Xpos, BX   ; Save for next time through\r
2710 \r
2711     MOV     BX, [BP].TPS_Ypos   ; Get Ypos\r
2712     PUSH    BX                  ; Set Ypos Parameter\r
2713 \r
2714     MOV     BX, [BP].TPS_ColorF ; Get Text Color\r
2715     PUSH    BX                  ; Set ColorF Parameter\r
2716 \r
2717     CALL    f TGPRINTC          ; Print Character!\r
2718     JMP     s @TPS_Print_It     ; Process next character\r
2719 \r
2720 @TPS_Exit:\r
2721     ;POPx    DI, SI, DS, BP      ; Restore Saved Registers\r
2722     pop di\r
2723         pop     si\r
2724         pop     ds\r
2725         pop     bp\r
2726     RET     12                  ; Exit and Clean up Stack\r
2727 \r
2728 TPRINT_STR  ENDP\r
2729 \r
2730 \r
2731 ;===========================================\r
2732 ;SET_DISPLAY_FONT(SEG FontData, FontNumber%)\r
2733 ;===========================================\r
2734 ;\r
2735 ; Allows the user to specify their own font data for\r
2736 ; wither the lower or upper 128 characters.\r
2737 ;\r
2738 ; ENTRY: FontData   = Far Pointer to Font Bitmaps\r
2739 ;        FontNumber = Which half of set this is\r
2740 ;                   = 0, Lower 128 characters\r
2741 ;                   = 1, Upper 128 characters\r
2742 ;\r
2743 ; EXIT:  No meaningful values returned\r
2744 ;\r
2745 \r
2746 SDF_STACK   STRUC\r
2747                 DW  ?   ; BP\r
2748                 DD  ?   ; Caller\r
2749     SDF_Which   DW  ?   ; Hi Table/Low Table Flag\r
2750     SDF_Font    DD  ?   ; Far Ptr to Font Table\r
2751 SDF_STACK   ENDS\r
2752 \r
2753     PUBLIC  SET_DISPLAY_FONT\r
2754 \r
2755 SET_DISPLAY_FONT    PROC    FAR\r
2756 \r
2757     PUSH    BP                  ; Preserve Registers\r
2758     MOV     BP, SP              ; Set up Stack Frame\r
2759 \r
2760     LES     DI, [BP].SDF_Font   ; Get Far Ptr to Font\r
2761 \r
2762     MOV     SI, o CHARSET_LOW   ; Assume Lower 128 chars\r
2763     TEST    [BP].SDF_Which, 1   ; Font #1 selected?\r
2764     JZ      @SDF_Set_Font       ; If not, skip ahead\r
2765 \r
2766     MOV     SI, o CHARSET_HI    ; Ah, really it's 128-255\r
2767 \r
2768 @SDF_Set_Font:\r
2769     MOV     [SI], DI            ; Set Font Pointer Offset\r
2770     MOV     [SI+2], ES          ; Set Font Pointer Segment\r
2771 \r
2772     POP     BP                  ; Restore Registers\r
2773     RET     6                   ; We are Done.. Outa here\r
2774 \r
2775 SET_DISPLAY_FONT    ENDP\r
2776 \r
2777 \r
2778     ; ===== BITMAP (SPRITE) DISPLAY ROUTINES =====\r
2779 \r
2780 ;======================================================\r
2781 ;DRAW_BITMAP (SEG Image, Xpos%, Ypos%, Width%, Height%)\r
2782 ;======================================================\r
2783 ;\r
2784 ; Draws a variable sized Graphics Bitmap such as a\r
2785 ; picture or an Icon on the current Display Page in\r
2786 ; Mode X.  The Bitmap is stored in a linear byte array\r
2787 ; corresponding to (0,0) (1,0), (2,0) .. (Width, Height)\r
2788 ; This is the same linear manner as mode 13h graphics.\r
2789 ;\r
2790 ; ENTRY: Image  = Far Pointer to Bitmap Data\r
2791 ;        Xpos   = X position to Place Upper Left pixel at\r
2792 ;        Ypos   = Y position to Place Upper Left pixel at\r
2793 ;        Width  = Width of the Bitmap in Pixels\r
2794 ;        Height = Height of the Bitmap in Pixels\r
2795 ;\r
2796 ; EXIT:  No meaningful values returned\r
2797 ;\r
2798 \r
2799 DB_STACK    STRUC\r
2800     DB_LineO    DW  ?   ; Offset to Next Line\r
2801     DB_PixCount DW  ?   ; (Minimum) # of Pixels/Line\r
2802     DB_Start    DW  ?   ; Addr of Upper Left Pixel\r
2803     DB_PixSkew  DW  ?   ; # of bytes to Adjust EOL\r
2804     DB_SkewFlag DW  ?   ; Extra Pix on Plane Flag\r
2805                 DW  ?x1 ; DI, SI, DS, BP\r
2806                 DW  ?x1 ; DI, SI, DS, BP\r
2807                 DW  ?x1 ; DS, DI, SI, BP\r
2808                 DW  ?x1 ; DS, DI, SI, BP\r
2809                 DD  ?   ; Caller\r
2810     DB_Height   DW  ?   ; Height of Bitmap in Pixels\r
2811     DB_Width    DW  ?   ; Width of Bitmap in Pixels\r
2812     DB_Ypos     DW  ?   ; Y position to Draw Bitmap at\r
2813     DB_Xpos     DW  ?   ; X position to Draw Bitmap at\r
2814     DB_Image    DD  ?   ; Far Pointer to Graphics Bitmap\r
2815 DB_STACK    ENDS\r
2816 \r
2817         PUBLIC    DRAW_BITMAP\r
2818 \r
2819 DRAW_BITMAP PROC    FAR\r
2820 \r
2821     ;PUSHx   BP, DS, SI, DI      ; Preserve Important Registers\r
2822     push        bp\r
2823         push    ds\r
2824         push    si\r
2825         push    di\r
2826     SUB     SP, 10              ; Allocate workspace\r
2827     MOV     BP, SP              ; Set up Stack Frame\r
2828 \r
2829     LES     DI, d CURRENT_PAGE  ; Point to Active VGA Page\r
2830     CLD                         ; Direction Flag = Forward\r
2831 \r
2832     MOV     AX, [BP].DB_Ypos    ; Get UL Corner Ypos\r
2833     MUL     SCREEN_WIDTH        ; AX = Offset to Line Ypos\r
2834 \r
2835     MOV     BX, [BP].DB_Xpos    ; Get UL Corner Xpos\r
2836     MOV     CL, BL              ; Save Plane # in CL\r
2837     SHR     BX, 1               ; Xpos/4 = Offset Into Line\r
2838     SHR     BX, 1               ; Xpos/4 = Offset Into Line\r
2839 \r
2840     ADD     DI, AX              ; ES:DI -> Start of Line\r
2841     ADD     DI, BX              ; ES:DI -> Upper Left Pixel\r
2842     MOV     [BP].DB_Start, DI   ; Save Starting Addr\r
2843 \r
2844     ; Compute line to line offset\r
2845 \r
2846     MOV     BX, [BP].DB_Width   ; Get Width of Image\r
2847     MOV     DX, BX              ; Save Copy in DX\r
2848     SHR     BX, 1               ; /4 = width in bands\r
2849     SHR     BX, 1               ; /4 = width in bands\r
2850     MOV     AX, SCREEN_WIDTH    ; Get Screen Width\r
2851     SUB     AX, BX              ; - (Bitmap Width/4)\r
2852 \r
2853     MOV     [BP].DB_LineO, AX       ; Save Line Width offset\r
2854     MOV     [BP].DB_PixCount, BX    ; Minimum # pix to copy\r
2855 \r
2856     AND     DX, PLANE_BITS          ; Get "partial band" size (0-3)\r
2857     MOV     [BP].DB_PixSkew, DX     ; Also End of Line Skew\r
2858     MOV     [BP].DB_SkewFlag, DX    ; Save as Flag/Count\r
2859 \r
2860     AND     CX, PLANE_BITS      ; CL = Starting Plane #\r
2861     MOV     AX, MAP_MASK_PLANE2 ; Plane Mask & Plane Select\r
2862     SHL     AH, CL              ; Select correct Plane\r
2863     OUT_16  SC_Index, AX        ; Select Plane...\r
2864     MOV     BH, AH              ; BH = Saved Plane Mask\r
2865     MOV     BL, 4               ; BL = Planes to Copy\r
2866 \r
2867 @DB_COPY_PLANE:\r
2868 \r
2869     LDS     SI, [BP].DB_Image   ; DS:SI-> Source Image\r
2870     MOV     DX, [BP].DB_Height  ; # of Lines to Copy\r
2871     MOV     DI, [BP].DB_Start   ; ES:DI-> Dest pos\r
2872 \r
2873 @DB_COPY_LINE:\r
2874     MOV     CX, [BP].DB_PixCount    ; Min # to copy\r
2875 \r
2876     TEST    CL, 0FCh            ; 16+PixWide?\r
2877     JZ      @DB_COPY_REMAINDER  ; Nope...\r
2878 \r
2879     ; Pixel Copy loop has been unrolled to x4\r
2880 \r
2881 @DB_COPY_LOOP:\r
2882     MOVSB                       ; Copy Bitmap Pixel\r
2883     ADD     SI, 3               ; Skip to Next Byte in same plane\r
2884     MOVSB                       ; Copy Bitmap Pixel\r
2885     ADD     SI, 3               ; Skip to Next Byte in same plane\r
2886     MOVSB                       ; Copy Bitmap Pixel\r
2887     ADD     SI, 3               ; Skip to Next Byte in same plane\r
2888     MOVSB                       ; Copy Bitmap Pixel\r
2889     ADD     SI, 3               ; Skip to Next Byte in same plane\r
2890 \r
2891     SUB     CL, 4               ; Pixels to Copy=-4\r
2892     TEST    CL, 0FCh            ; 4+ Pixels Left?\r
2893     JNZ     @DB_COPY_LOOP       ; if so, do another block\r
2894 \r
2895 @DB_COPY_REMAINDER:\r
2896     JCXZ    @DB_NEXT_LINE       ; Any Pixels left on line\r
2897 \r
2898 @DB_COPY2:\r
2899     MOVSB                       ; Copy Bitmap Pixel\r
2900     ADD     SI,3                ; Skip to Next Byte in same plane\r
2901     LOOPx   CX, @DB_COPY2       ; Pixels to Copy--, Loop until done\r
2902 \r
2903 @DB_NEXT_LINE:\r
2904 \r
2905     ; any Partial Pixels? (some planes only)\r
2906 \r
2907     OR      CX, [BP].DB_SkewFlag    ; Get Skew Count\r
2908     JZ      @DB_NEXT2               ; if no partial pixels\r
2909 \r
2910     MOVSB                       ; Copy Bitmap Pixel\r
2911     DEC     DI                  ; Back up to align\r
2912     DEC     SI                  ; Back up to align\r
2913 \r
2914 @DB_NEXT2:\r
2915     ADD     SI, [BP].DB_PixSkew ; Adjust Skew\r
2916     ADD     DI, [BP].DB_LineO   ; Set to Next Display Line\r
2917     LOOPx   DX, @DB_COPY_LINE   ; Lines to Copy--, Loop if more\r
2918 \r
2919     ; Copy Next Plane....\r
2920 \r
2921     DEC     BL                  ; Planes to Go--\r
2922     JZ      @DB_Exit            ; Hey! We are done\r
2923 \r
2924     ROL     BH, 1               ; Next Plane in line...\r
2925     OUT_8   SC_Data, BH         ; Select Plane\r
2926 \r
2927     CMP     AL, 12h             ; Carry Set if AL=11h\r
2928     ADC     [BP].DB_Start, 0    ; Screen Addr =+Carry\r
2929     INC     w [BP].DB_Image     ; Start @ Next Byte\r
2930 \r
2931     SUB     [BP].DB_SkewFlag, 1 ; Reduce Planes to Skew\r
2932     ADC     [BP].DB_SkewFlag, 0 ; Back to 0 if it was -1\r
2933 \r
2934     JMP     s @DB_COPY_PLANE    ; Go Copy the Next Plane\r
2935 \r
2936 @DB_Exit:\r
2937     ADD     SP, 10              ; Deallocate workspace\r
2938     ;POPx    DI, SI, DS, BP      ; Restore Saved Registers\r
2939     pop di\r
2940         pop     si\r
2941         pop     ds\r
2942         pop     bp\r
2943     RET     12                  ; Exit and Clean up Stack\r
2944 \r
2945 DRAW_BITMAP   ENDP\r
2946 \r
2947 \r
2948 ;=======================================================\r
2949 ;TDRAW_BITMAP (SEG Image, Xpos%, Ypos%, Width%, Height%)\r
2950 ;=======================================================\r
2951 ;\r
2952 ; Transparently Draws a variable sized Graphics Bitmap\r
2953 ; such as a picture or an Icon on the current Display Page\r
2954 ; in Mode X.  Pixels with a value of 0 are not drawn,\r
2955 ; leaving the previous "background" contents intact.\r
2956 ;\r
2957 ; The Bitmap format is the same as for the DRAW_BITMAP function.\r
2958 ;\r
2959 ; ENTRY: Image  = Far Pointer to Bitmap Data\r
2960 ;        Xpos   = X position to Place Upper Left pixel at\r
2961 ;        Ypos   = Y position to Place Upper Left pixel at\r
2962 ;        Width  = Width of the Bitmap in Pixels\r
2963 ;        Height = Height of the Bitmap in Pixels\r
2964 ;\r
2965 ; EXIT:  No meaningful values returned\r
2966 ;\r
2967 \r
2968 TB_STACK    STRUC\r
2969     TB_LineO    DW  ?   ; Offset to Next Line\r
2970     TB_PixCount DW  ?   ; (Minimum) # of Pixels/Line\r
2971     TB_Start    DW  ?   ; Addr of Upper Left Pixel\r
2972     TB_PixSkew  DW  ?   ; # of bytes to Adjust EOL\r
2973     TB_SkewFlag DW  ?   ; Extra Pix on Plane Flag\r
2974                 DW  ?x1 ; DI, SI, DS, BP\r
2975                 DW  ?x1 ; DI, SI, DS, BP\r
2976                 DW  ?x1 ; DS, DI, SI, BP\r
2977                 DW  ?x1 ; DS, DI, SI, BP\r
2978                 DD  ?   ; Caller\r
2979     TB_Height   DW  ?   ; Height of Bitmap in Pixels\r
2980     TB_Width    DW  ?   ; Width of Bitmap in Pixels\r
2981     TB_Ypos     DW  ?   ; Y position to Draw Bitmap at\r
2982     TB_Xpos     DW  ?   ; X position to Draw Bitmap at\r
2983     TB_Image    DD  ?   ; Far Pointer to Graphics Bitmap\r
2984 TB_STACK    ENDS\r
2985 \r
2986         PUBLIC    TDRAW_BITMAP\r
2987 \r
2988 TDRAW_BITMAP    PROC    FAR\r
2989 \r
2990     ;PUSHx   BP, DS, SI, DI      ; Preserve Important Registers\r
2991     push        bp\r
2992         push    ds\r
2993         push    si\r
2994         push    di\r
2995     SUB     SP, 10              ; Allocate workspace\r
2996     MOV     BP, SP              ; Set up Stack Frame\r
2997 \r
2998     LES     DI, d CURRENT_PAGE  ; Point to Active VGA Page\r
2999     CLD                         ; Direction Flag = Forward\r
3000 \r
3001     MOV     AX, [BP].TB_Ypos    ; Get UL Corner Ypos\r
3002     MUL     SCREEN_WIDTH        ; AX = Offset to Line Ypos\r
3003 \r
3004     MOV     BX, [BP].TB_Xpos    ; Get UL Corner Xpos\r
3005     MOV     CL, BL              ; Save Plane # in CL\r
3006     SHR     BX, 1               ; Xpos/4 = Offset Into Line\r
3007     SHR     BX, 1               ; Xpos/4 = Offset Into Line\r
3008 \r
3009     ADD     DI, AX              ; ES:DI -> Start of Line\r
3010     ADD     DI, BX              ; ES:DI -> Upper Left Pixel\r
3011     MOV     [BP].TB_Start, DI   ; Save Starting Addr\r
3012 \r
3013     ; Compute line to line offset\r
3014 \r
3015     MOV     BX, [BP].TB_Width   ; Get Width of Image\r
3016     MOV     DX, BX              ; Save Copy in DX\r
3017     SHR     BX, 1               ; /4 = width in bands\r
3018     SHR     BX, 1               ; /4 = width in bands\r
3019     MOV     AX, SCREEN_WIDTH    ; Get Screen Width\r
3020     SUB     AX, BX              ; - (Bitmap Width/4)\r
3021 \r
3022     MOV     [BP].TB_LineO, AX       ; Save Line Width offset\r
3023     MOV     [BP].TB_PixCount, BX    ; Minimum # pix to copy\r
3024 \r
3025     AND     DX, PLANE_BITS          ; Get "partial band" size (0-3)\r
3026     MOV     [BP].TB_PixSkew, DX     ; Also End of Line Skew\r
3027     MOV     [BP].TB_SkewFlag, DX    ; Save as Flag/Count\r
3028 \r
3029     AND     CX, PLANE_BITS      ; CL = Starting Plane #\r
3030     MOV     AX, MAP_MASK_PLANE2 ; Plane Mask & Plane Select\r
3031     SHL     AH, CL              ; Select correct Plane\r
3032     OUT_16  SC_Index, AX        ; Select Plane...\r
3033     MOV     BH, AH              ; BH = Saved Plane Mask\r
3034     MOV     BL, 4               ; BL = Planes to Copy\r
3035 \r
3036 @TB_COPY_PLANE:\r
3037 \r
3038     LDS     SI, [BP].TB_Image   ; DS:SI-> Source Image\r
3039     MOV     DX, [BP].TB_Height  ; # of Lines to Copy\r
3040     MOV     DI, [BP].TB_Start   ; ES:DI-> Dest pos\r
3041 \r
3042     ; Here AH is set with the value to be considered\r
3043     ; "Transparent".  It can be changed!\r
3044 \r
3045     MOV     AH, 0               ; Value to Detect 0\r
3046 \r
3047 @TB_COPY_LINE:\r
3048     MOV     CX, [BP].TB_PixCount    ; Min # to copy\r
3049 \r
3050     TEST    CL, 0FCh            ; 16+PixWide?\r
3051     JZ      @TB_COPY_REMAINDER  ; Nope...\r
3052 \r
3053     ; Pixel Copy loop has been unrolled to x4\r
3054 \r
3055 @TB_COPY_LOOP:\r
3056     LODSB                       ; Get Pixel Value in AL\r
3057     ADD     SI, 3               ; Skip to Next Byte in same plane\r
3058     CMP     AL, AH              ; It is "Transparent"?\r
3059     JE      @TB_SKIP_01         ; Skip ahead if so\r
3060     MOV     ES:[DI], AL         ; Copy Pixel to VGA screen\r
3061 \r
3062 @TB_SKIP_01:\r
3063     LODSB                       ; Get Pixel Value in AL\r
3064     ADD     SI, 3               ; Skip to Next Byte in same plane\r
3065     CMP     AL, AH              ; It is "Transparent"?\r
3066     JE      @TB_SKIP_02         ; Skip ahead if so\r
3067     MOV     ES:[DI+1], AL       ; Copy Pixel to VGA screen\r
3068 \r
3069 @TB_SKIP_02:\r
3070     LODSB                       ; Get Pixel Value in AL\r
3071     ADD     SI, 3               ; Skip to Next Byte in same plane\r
3072     CMP     AL, AH              ; It is "Transparent"?\r
3073     JE      @TB_SKIP_03         ; Skip ahead if so\r
3074     MOV     ES:[DI+2], AL       ; Copy Pixel to VGA screen\r
3075 \r
3076 @TB_SKIP_03:\r
3077     LODSB                       ; Get Pixel Value in AL\r
3078     ADD     SI, 3               ; Skip to Next Byte in same plane\r
3079     CMP     AL, AH              ; It is "Transparent"?\r
3080     JE      @TB_SKIP_04         ; Skip ahead if so\r
3081     MOV     ES:[DI+3], AL       ; Copy Pixel to VGA screen\r
3082 \r
3083 @TB_SKIP_04:\r
3084     ADD     DI, 4               ; Adjust Pixel Write Location\r
3085     SUB     CL, 4               ; Pixels to Copy=-4\r
3086     TEST    CL, 0FCh            ; 4+ Pixels Left?\r
3087     JNZ     @TB_COPY_LOOP       ; if so, do another block\r
3088 \r
3089 @TB_COPY_REMAINDER:\r
3090     JCXZ    @TB_NEXT_LINE       ; Any Pixels left on line\r
3091 \r
3092 @TB_COPY2:\r
3093     LODSB                       ; Get Pixel Value in AL\r
3094     ADD     SI, 3               ; Skip to Next Byte in same plane\r
3095     CMP     AL, AH              ; It is "Transparent"?\r
3096     JE      @TB_SKIP_05         ; Skip ahead if so\r
3097     MOV     ES:[DI], AL         ; Copy Pixel to VGA screen\r
3098 \r
3099 @TB_SKIP_05:\r
3100     INC     DI                  ; Advance Dest Addr\r
3101     LOOPx   CX, @TB_COPY2       ; Pixels to Copy--, Loop until done\r
3102 \r
3103 @TB_NEXT_LINE:\r
3104 \r
3105     ; any Partial Pixels? (some planes only)\r
3106 \r
3107     OR      CX, [BP].TB_SkewFlag    ; Get Skew Count\r
3108     JZ      @TB_NEXT2               ; if no partial pixels\r
3109 \r
3110     LODSB                       ; Get Pixel Value in AL\r
3111     DEC     SI                  ; Backup to Align\r
3112     CMP     AL, AH              ; It is "Transparent"?\r
3113     JE      @TB_NEXT2           ; Skip ahead if so\r
3114     MOV     ES:[DI], AL         ; Copy Pixel to VGA screen\r
3115 \r
3116 @TB_NEXT2:\r
3117     ADD     SI, [BP].TB_PixSkew ; Adjust Skew\r
3118     ADD     DI, [BP].TB_LineO   ; Set to Next Display Line\r
3119     LOOPx   DX, @TB_COPY_LINE   ; Lines to Copy--, Loop if More\r
3120 \r
3121     ;Copy Next Plane....\r
3122 \r
3123     DEC     BL                  ; Planes to Go--\r
3124     JZ      @TB_Exit            ; Hey! We are done\r
3125 \r
3126     ROL     BH, 1               ; Next Plane in line...\r
3127     OUT_8   SC_Data, BH         ; Select Plane\r
3128 \r
3129     CMP     AL, 12h             ; Carry Set if AL=11h\r
3130     ADC     [BP].TB_Start, 0    ; Screen Addr =+Carry\r
3131     INC     w [BP].TB_Image     ; Start @ Next Byte\r
3132 \r
3133     SUB     [BP].TB_SkewFlag, 1 ; Reduce Planes to Skew\r
3134     ADC     [BP].TB_SkewFlag, 0 ; Back to 0 if it was -1\r
3135 \r
3136     JMP     @TB_COPY_PLANE      ; Go Copy the next Plane\r
3137 \r
3138 @TB_Exit:\r
3139     ADD     SP, 10              ; Deallocate workspace\r
3140     ;POPx    DI, SI, DS, BP      ; Restore Saved Registers\r
3141     pop di\r
3142         pop     si\r
3143         pop     ds\r
3144         pop     bp\r
3145     RET     12                  ; Exit and Clean up Stack\r
3146 \r
3147 TDRAW_BITMAP    ENDP\r
3148 \r
3149 \r
3150     ; ==== VIDEO MEMORY to VIDEO MEMORY COPY ROUTINES =====\r
3151 \r
3152 ;==================================\r
3153 ;COPY_PAGE (SourcePage%, DestPage%)\r
3154 ;==================================\r
3155 ;\r
3156 ; Duplicate on display page onto another\r
3157 ;\r
3158 ; ENTRY: SourcePage = Display Page # to Duplicate\r
3159 ;        DestPage   = Display Page # to hold copy\r
3160 ;\r
3161 ; EXIT:  No meaningful values returned\r
3162 ;\r
3163 \r
3164 CP_STACK    STRUC\r
3165                 DW  ?x1 ; DI, SI, DS, BP\r
3166                 DW  ?x1 ; DI, SI, DS, BP\r
3167                 DW  ?x1 ; DS, DI, SI, BP\r
3168                 DW  ?x1 ; DS, DI, SI, BP\r
3169                 DD  ?   ; Caller\r
3170     CP_DestP    DW  ?   ; Page to hold copied image\r
3171     CP_SourceP  DW  ?   ; Page to Make copy from\r
3172 CP_STACK    ENDS\r
3173 \r
3174         PUBLIC    COPY_PAGE\r
3175 \r
3176 COPY_PAGE   PROC    FAR\r
3177 \r
3178     ;PUSHx   BP, DS, SI, DI      ; Preserve Important Registers\r
3179     push        bp\r
3180         push    ds\r
3181         push    si\r
3182         push    di\r
3183     MOV     BP, SP              ; Set up Stack Frame\r
3184     CLD                         ; Block Xfer Forwards\r
3185 \r
3186     ; Make sure Page #'s are valid\r
3187 \r
3188     MOV     AX, [BP].CP_SourceP ; Get Source Page #\r
3189     CMP     AX, LAST_PAGE       ; is it > Max Page #?\r
3190     JAE     @CP_Exit            ; if so, abort\r
3191 \r
3192     MOV     BX, [BP].CP_DestP   ; Get Destination Page #\r
3193     CMP     BX, LAST_PAGE       ; is it > Max Page #?\r
3194     JAE     @CP_Exit            ; if so, abort\r
3195 \r
3196     CMP     AX, BX              ; Pages #'s the same?\r
3197     JE      @CP_Exit            ; if so, abort\r
3198 \r
3199     ; Setup DS:SI and ES:DI to Video Pages\r
3200 \r
3201     SHL     BX, 1               ; Scale index to Word\r
3202     MOV     DI, PAGE_ADDR[BX]   ; Offset to Dest Page\r
3203 \r
3204     MOV     BX, AX              ; Index to Source page\r
3205     SHL     BX, 1               ; Scale index to Word\r
3206     MOV     SI, PAGE_ADDR[BX]   ; Offset to Source Page\r
3207 \r
3208     MOV     CX, PAGE_SIZE       ; Get size of Page\r
3209     MOV     AX, CURRENT_SEGMENT ; Get Video Mem Segment\r
3210     MOV     ES, AX              ; ES:DI -> Dest Page\r
3211     MOV     DS, AX              ; DS:SI -> Source Page\r
3212 \r
3213     ; Setup VGA registers for Mem to Mem copy\r
3214 \r
3215     OUT_16  GC_Index, LATCHES_ON    ; Data from Latches = on\r
3216     OUT_16  SC_Index, ALL_PLANES_ON ; Copy all Planes\r
3217 \r
3218     ; Note.. Do *NOT* use MOVSW or MOVSD - they will\r
3219     ; Screw with the latches which are 8 bits x 4\r
3220 \r
3221     REP     MOVSB               ; Copy entire Page!\r
3222 \r
3223     ; Reset VGA for normal memory access\r
3224 \r
3225     OUT_16  GC_Index, LATCHES_OFF   ; Data from Latches = off\r
3226 \r
3227 @CP_Exit:\r
3228     ;POPx    DI, SI, DS, BP      ; Restore Saved Registers\r
3229     pop di\r
3230         pop     si\r
3231         pop     ds\r
3232         pop     bp\r
3233     RET     4                   ; Exit and Clean up Stack\r
3234 \r
3235 COPY_PAGE   ENDP\r
3236 \r
3237 \r
3238 ;==========================================================================\r
3239 ;COPY_BITMAP (SourcePage%, X1%, Y1%, X2%, Y2%, DestPage%, DestX1%, DestY1%)\r
3240 ;==========================================================================\r
3241 ;\r
3242 ; Copies a Bitmap Image from one Display Page to Another\r
3243 ; This Routine is Limited to copying Images with the same\r
3244 ; Plane Alignment.  To Work: (X1 MOD 4) must = (DestX1 MOD 4)\r
3245 ; Copying an Image to the Same Page is supported, but results\r
3246 ; may be defined when the when the rectangular areas\r
3247 ; (X1, Y1) - (X2, Y2) and (DestX1, DestY1) -\r
3248 ; (DestX1+(X2-X1), DestY1+(Y2-Y1)) overlap...\r
3249 ; No Paramter checking to done to insure that\r
3250 ; X2 >= X1 and Y2 >= Y1.  Be Careful...\r
3251 ;\r
3252 ; ENTRY: SourcePage = Display Page # with Source Image\r
3253 ;        X1         = Upper Left Xpos of Source Image\r
3254 ;        Y1         = Upper Left Ypos of Source Image\r
3255 ;        X2         = Lower Right Xpos of Source Image\r
3256 ;        Y2         = Lower Right Ypos of Source Image\r
3257 ;        DestPage   = Display Page # to copy Image to\r
3258 ;        DestX1     = Xpos to Copy UL Corner of Image to\r
3259 ;        DestY1     = Ypos to Copy UL Corner of Image to\r
3260 ;\r
3261 ; EXIT:  AX = Success Flag:   0 = Failure / -1= Success\r
3262 ;\r
3263 \r
3264 CB_STACK    STRUC\r
3265     CB_Height   DW  ?   ; Height of Image in Lines\r
3266     CB_Width    DW  ?   ; Width of Image in "bands"\r
3267                 DW  ?x1 ; DI, SI, DS, BP\r
3268                 DW  ?x1 ; DI, SI, DS, BP\r
3269                 DW  ?x1 ; DS, DI, SI, BP\r
3270                 DW  ?x1 ; DS, DI, SI, BP\r
3271                 DD  ?   ; Caller\r
3272     CB_DestY1   DW  ?   ; Destination Ypos\r
3273     CB_DestX1   DW  ?   ; Destination Xpos\r
3274     CB_DestP    DW  ?   ; Page to Copy Bitmap To\r
3275     CB_Y2       DW  ?   ; LR Ypos of Image\r
3276     CB_X2       DW  ?   ; LR Xpos of Image\r
3277     CB_Y1       DW  ?   ; UL Ypos of Image\r
3278     CB_X1       DW  ?   ; UL Xpos of Image\r
3279     CB_SourceP  DW  ?   ; Page containing Source Bitmap\r
3280 CB_STACK    ENDS\r
3281 \r
3282         PUBLIC    COPY_BITMAP\r
3283 \r
3284 COPY_BITMAP PROC    FAR\r
3285 \r
3286     ;PUSHx   BP, DS, SI, DI      ; Preserve Important Registers\r
3287     push        bp\r
3288         push    ds\r
3289         push    si\r
3290         push    di\r
3291     SUB     SP, 4               ; Allocate WorkSpace on Stack\r
3292     MOV     BP, SP              ; Set up Stack Frame\r
3293 \r
3294     ; Prep Registers (and keep jumps short!)\r
3295 \r
3296     MOV     ES, CURRENT_SEGMENT ; ES -> VGA Ram\r
3297     CLD                         ; Block Xfer Forwards\r
3298 \r
3299     ; Make sure Parameters are valid\r
3300 \r
3301     MOV     BX, [BP].CB_SourceP ; Get Source Page #\r
3302     CMP     BX, LAST_PAGE       ; is it > Max Page #?\r
3303     JAE     @CB_Abort           ; if so, abort\r
3304 \r
3305     MOV     CX, [BP].CB_DestP   ; Get Destination Page #\r
3306     CMP     CX, LAST_PAGE       ; is it > Max Page #?\r
3307     JAE     @CB_Abort           ; if so, abort\r
3308 \r
3309     MOV     AX, [BP].CB_X1      ; Get Source X1\r
3310     XOR     AX, [BP].CB_DestX1  ; Compare Bits 0-1\r
3311     AND     AX, PLANE_BITS      ; Check Plane Bits\r
3312     JNZ     @CB_Abort           ; They should cancel out\r
3313 \r
3314     ; Setup for Copy processing\r
3315 \r
3316     OUT_8   SC_INDEX, MAP_MASK      ; Set up for Plane Select\r
3317     OUT_16  GC_Index, LATCHES_ON    ; Data from Latches = on\r
3318 \r
3319     ; Compute Info About Images, Setup ES:SI & ES:DI\r
3320 \r
3321     MOV     AX, [BP].CB_Y2      ; Height of Bitmap in lines\r
3322     SUB     AX, [BP].CB_Y1      ; is Y2 - Y1 + 1\r
3323     INC     AX                  ; (add 1 since were not 0 based)\r
3324     MOV     [BP].CB_Height, AX  ; Save on Stack for later use\r
3325 \r
3326     MOV     AX, [BP].CB_X2      ; Get # of "Bands" of 4 Pixels\r
3327     MOV     DX, [BP].CB_X1      ; the Bitmap Occupies as X2-X1\r
3328     SHR     AX, 1               ; Get X2 Band (X2 / 4)\r
3329     SHR     DX, 1               ; Get X1 Band (X1 / 4)\r
3330     SHR     AX, 1               ; Get X2 Band (X2 / 4)\r
3331     SHR     DX, 1               ; Get X1 Band (X1 / 4)\r
3332     SUB     AX, DX              ; AX = # of Bands - 1\r
3333     INC     AX                  ; AX = # of Bands\r
3334     MOV     [BP].CB_Width, AX   ; Save on Stack for later use\r
3335 \r
3336     SHL     BX, 1               ; Scale Source Page to Word\r
3337     MOV     SI, PAGE_ADDR[BX]   ; SI = Offset of Source Page\r
3338     MOV     AX, [BP].CB_Y1      ; Get Source Y1 Line\r
3339     MUL     SCREEN_WIDTH        ; AX = Offset to Line Y1\r
3340     ADD     SI, AX              ; SI = Offset to Line Y1\r
3341     MOV     AX, [BP].CB_X1      ; Get Source X1\r
3342     SHR     AX, 1               ; X1 / 4 = Byte offset\r
3343     SHR     AX, 1               ; X1 / 4 = Byte offset\r
3344     ADD     SI, AX              ; SI = Byte Offset to (X1,Y1)\r
3345 \r
3346     MOV     BX, CX              ; Dest Page Index to BX\r
3347     SHL     BX, 1               ; Scale Source Page to Word\r
3348     MOV     DI, PAGE_ADDR[BX]   ; DI = Offset of Dest Page\r
3349     MOV     AX, [BP].CB_DestY1  ; Get Dest Y1 Line\r
3350     MUL     SCREEN_WIDTH        ; AX = Offset to Line Y1\r
3351     ADD     DI, AX              ; DI = Offset to Line Y1\r
3352     MOV     AX, [BP].CB_DestX1  ; Get Dest X1\r
3353     SHR     AX, 1              ; X1 / 4 = Byte offset\r
3354     SHR     AX, 1               ; X1 / 4 = Byte offset\r
3355     ADD     DI, AX              ; DI = Byte Offset to (D-X1,D-Y1)\r
3356 \r
3357     MOV     CX, [BP].CB_Width   ; CX = Width of Image (Bands)\r
3358     DEC     CX                  ; CX = 1?\r
3359     JE      @CB_Only_One_Band   ; 0 Means Image Width of 1 Band\r
3360 \r
3361     MOV     BX, [BP].CB_X1      ; Get Source X1\r
3362     AND     BX, PLANE_BITS      ; Aligned? (bits 0-1 = 00?)\r
3363     JZ      @CB_Check_Right     ; if so, check right alignment\r
3364     JNZ     @CB_Left_Band       ; not aligned? well..\r
3365 \r
3366 @CB_Abort:\r
3367     mov ax,0                  ; Return False (Failure)\r
3368     JMP     @CB_Exit            ; and Finish Up\r
3369 \r
3370     ; Copy when Left & Right Clip Masks overlap...\r
3371 \r
3372 @CB_Only_One_Band:\r
3373     MOV     BX, [BP].CB_X1          ; Get Left Clip Mask\r
3374     AND     BX, PLANE_BITS          ; Mask out Row #\r
3375     MOV     AL, Left_Clip_Mask[BX]  ; Get Left Edge Mask\r
3376     MOV     BX, [BP].CB_X2          ; Get Right Clip Mask\r
3377     AND     BX, PLANE_BITS          ; Mask out Row #\r
3378     AND     AL, Right_Clip_Mask[BX] ; Get Right Edge Mask byte\r
3379 \r
3380     OUT_8   SC_Data, AL         ; Clip For Left & Right Masks\r
3381 \r
3382     MOV     CX, [BP].CB_Height  ; CX = # of Lines to Copy\r
3383     MOV     DX, SCREEN_WIDTH    ; DX = Width of Screen\r
3384     mov bx,0                  ; BX = Offset into Image\r
3385 \r
3386 @CB_One_Loop:\r
3387     MOV     AL, ES:[SI+BX]      ; Load Latches\r
3388     MOV     ES:[DI+BX], AL      ; Unload Latches\r
3389     ADD     BX, DX              ; Advance Offset to Next Line\r
3390     LOOPjz  CX, @CB_One_Done    ; Exit Loop if Finished\r
3391 \r
3392     MOV     AL, ES:[SI+BX]      ; Load Latches\r
3393     MOV     ES:[DI+BX], AL      ; Unload Latches\r
3394     ADD     BX, DX              ; Advance Offset to Next Line\r
3395     LOOPx   CX, @CB_One_Loop    ; Loop until Finished\r
3396 \r
3397 @CB_One_Done:\r
3398     JMP     @CB_Finish          ; Outa Here!\r
3399 \r
3400     ; Copy Left Edge of Bitmap\r
3401 \r
3402 @CB_Left_Band:\r
3403 \r
3404     OUT_8   SC_Data, Left_Clip_Mask[BX] ; Set Left Edge Plane Mask\r
3405 \r
3406     MOV     CX, [BP].CB_Height  ; CX = # of Lines to Copy\r
3407     MOV     DX, SCREEN_WIDTH    ; DX = Width of Screen\r
3408     mov bx,0                  ; BX = Offset into Image\r
3409 \r
3410 @CB_Left_Loop:\r
3411     MOV     AL, ES:[SI+BX]      ; Load Latches\r
3412     MOV     ES:[DI+BX], AL      ; Unload Latches\r
3413     ADD     BX, DX              ; Advance Offset to Next Line\r
3414     LOOPjz  CX, @CB_Left_Done   ; Exit Loop if Finished\r
3415 \r
3416     MOV     AL, ES:[SI+BX]      ; Load Latches\r
3417     MOV     ES:[DI+BX], AL      ; Unload Latches\r
3418     ADD     BX, DX              ; Advance Offset to Next Line\r
3419     LOOPx   CX, @CB_Left_Loop   ; Loop until Finished\r
3420 \r
3421 @CB_Left_Done:\r
3422     INC     DI                  ; Move Dest Over 1 band\r
3423     INC     SI                  ; Move Source Over 1 band\r
3424     DEC     [BP].CB_Width       ; Band Width--\r
3425 \r
3426     ; Determine if Right Edge of Bitmap needs special copy\r
3427 \r
3428 @CB_Check_Right:\r
3429     MOV     BX, [BP].CB_X2      ; Get Source X2\r
3430     AND     BX, PLANE_BITS      ; Aligned? (bits 0-1 = 11?)\r
3431     CMP     BL, 03h             ; Plane = 3?\r
3432     JE      @CB_Copy_Middle     ; Copy the Middle then!\r
3433 \r
3434     ; Copy Right Edge of Bitmap\r
3435 \r
3436 @CB_Right_Band:\r
3437 \r
3438     OUT_8   SC_Data, Right_Clip_Mask[BX]    ; Set Right Edge Plane Mask\r
3439 \r
3440     DEC     [BP].CB_Width       ; Band Width--\r
3441     MOV     CX, [BP].CB_Height  ; CX = # of Lines to Copy\r
3442     MOV     DX, SCREEN_WIDTH    ; DX = Width of Screen\r
3443     MOV     BX, [BP].CB_Width   ; BX = Offset to Right Edge\r
3444 \r
3445 @CB_Right_Loop:\r
3446     MOV     AL, ES:[SI+BX]      ; Load Latches\r
3447     MOV     ES:[DI+BX], AL      ; Unload Latches\r
3448     ADD     BX, DX              ; Advance Offset to Next Line\r
3449     LOOPjz  CX, @CB_Right_Done  ; Exit Loop if Finished\r
3450 \r
3451     MOV     AL, ES:[SI+BX]      ; Load Latches\r
3452     MOV     ES:[DI+BX], AL      ; Unload Latches\r
3453     ADD     BX, DX              ; Advance Offset to Next Line\r
3454     LOOPx   CX, @CB_Right_Loop  ; Loop until Finished\r
3455 \r
3456 @CB_Right_Done:\r
3457 \r
3458     ; Copy the Main Block of the Bitmap\r
3459 \r
3460 @CB_Copy_Middle:\r
3461 \r
3462     MOV     CX, [BP].CB_Width   ; Get Width Remaining\r
3463     JCXZ    @CB_Finish          ; Exit if Done\r
3464 \r
3465     OUT_8   SC_Data, ALL_PLANES ; Copy all Planes\r
3466 \r
3467     MOV     DX, SCREEN_WIDTH    ; Get Width of Screen minus\r
3468     SUB     DX, CX              ; Image width (for Adjustment)\r
3469     MOV     AX, [BP].CB_Height  ; AX = # of Lines to Copy\r
3470     MOV     BX, CX              ; BX = Quick REP reload count\r
3471     MOV     CX, ES              ; Move VGA Segment\r
3472     MOV     DS, CX              ; Into DS\r
3473 \r
3474     ; Actual Copy Loop.  REP MOVSB does the work\r
3475 \r
3476 @CB_Middle_Copy:\r
3477     MOV     CX, BX              ; Recharge Rep Count\r
3478     REP     MOVSB               ; Move Bands\r
3479     LOOPjz  AX, @CB_Finish      ; Exit Loop if Finished\r
3480 \r
3481     ADD     SI, DX              ; Adjust DS:SI to Next Line\r
3482     ADD     DI, DX              ; Adjust ES:DI to Next Line\r
3483 \r
3484     MOV     CX, BX              ; Recharge Rep Count\r
3485     REP     MOVSB               ; Move Bands\r
3486 \r
3487     ADD     SI, DX              ; Adjust DS:SI to Next Line\r
3488     ADD     DI, DX              ; Adjust ES:DI to Next Line\r
3489     LOOPx   AX, @CB_Middle_Copy ; Copy Lines until Done\r
3490 \r
3491 @CB_Finish:\r
3492     OUT_16  GC_Index, LATCHES_OFF   ; Data from Latches = on\r
3493 \r
3494 @CB_Exit:\r
3495     ADD     SP, 04              ; Deallocate stack workspace\r
3496     ;POPx    DI, SI, DS, BP      ; Restore Saved Registers\r
3497     pop di\r
3498         pop     si\r
3499         pop     ds\r
3500         pop     bp\r
3501     RET     16                  ; Exit and Clean up Stack\r
3502 \r
3503 COPY_BITMAP ENDP\r
3504 \r
3505     END                         ; End of Code Segment\r