OSDN Git Service

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