1 ;-----------------------------------------------------------------------
\r
4 ; Initialization, panning and split screen functions for all MODE X 256
\r
11 ; ****** XLIB - Mode X graphics library ****************
\r
12 ; ****** ****************
\r
13 ; ****** Written By Themie Gouthas ****************
\r
15 ; egg@dstos3.dsto.gov.au
\r
16 ; teg@bart.dsto.gov.au
\r
19 ; 26-9-92: Pel panning code added
\r
20 ; Dates forgotten: Numerous ;^)
\r
21 ; 05-10-93: Timer synchronized vsync handling extensions
\r
22 ; and tripple buffering - Tore Jahn Bastiansen
\r
23 ; (toreba@ifi.uio.no) for the
\r
24 ;-----------------------------------------------------------------------
\r
34 ; Mode X CRTC register tweaks for various resolutions
\r
39 db 8 ; Number of CRTC Registers to update
\r
40 dw 05f00h ; horz total
\r
41 dw 03f01h ; horz displayed
\r
42 dw 04202h ; start horz blanking
\r
43 dw 09f03h ; end horz blanking
\r
44 dw 04c04h ; start h sync
\r
45 dw 00005h ; end h sync
\r
46 dw 00014h ; turn off dword mode
\r
47 dw 0e317h ; turn on byte mode
\r
54 db 16 ; Number of CRTC Registers to update
\r
55 dw 05f00h ; horz total
\r
56 dw 03f01h ; horz displayed
\r
57 dw 04202h ; start horz blanking
\r
58 dw 09f03h ; end horz blanking
\r
59 dw 04c04h ; start h sync
\r
60 dw 00005h ; end h sync
\r
61 dw 00d06h ; vertical total
\r
62 dw 03e07h ; overflow (bit 8 of vertical counts)
\r
63 dw 04109h ; cell height (2 to double-scan)
\r
64 dw 0ea10h ; v sync start
\r
65 dw 0ac11h ; v sync end and protect cr0-cr7
\r
66 dw 0df12h ; vertical displayed
\r
67 dw 00014h ; turn off dword mode
\r
68 dw 0e715h ; v blank start
\r
69 dw 00616h ; v blank end
\r
70 dw 0e317h ; turn on byte mode
\r
76 db 00 ; 0e3h ; dot clock
\r
77 db 02 ; Number of CRTC Registers to update
\r
78 dw 00014h ; turn off dword mode
\r
79 dw 0e317h ; turn on byte mode
\r
85 db 10 ; Number of CRTC Registers to update
\r
86 dw 00d06h ; vertical total
\r
87 dw 03e07h ; overflow (bit 8 of vertical counts)
\r
88 dw 04109h ; cell height (2 to double-scan)
\r
89 dw 0ea10h ; v sync start
\r
90 dw 0ac11h ; v sync end and protect cr0-cr7
\r
91 dw 0df12h ; vertical displayed
\r
92 dw 00014h ; turn off dword mode
\r
93 dw 0e715h ; v blank start
\r
94 dw 00616h ; v blank end
\r
95 dw 0e317h ; turn on byte mode
\r
100 db 0e7h ; dot clock
\r
101 db 08 ; Number of CRTC Registers to update
\r
102 dw 06b00h ; horz total
\r
103 dw 05901h ; horz displayed
\r
104 dw 05a02h ; start horz blanking
\r
105 dw 08e03h ; end horz blanking
\r
106 dw 05e04h ; start h sync
\r
107 dw 08a05h ; end h sync
\r
108 dw 00014h ; turn off dword mode
\r
109 dw 0e317h ; turn on byte mode
\r
113 X360Y240 label word
\r
114 db 0e7h ; dot clock
\r
115 db 17 ; Number of CRTC Registers to update
\r
116 dw 06b00h ; horz total
\r
117 dw 05901h ; horz displayed
\r
118 dw 05a02h ; start horz blanking
\r
119 dw 08e03h ; end horz blanking
\r
120 dw 05e04h ; start h sync
\r
121 dw 08a05h ; end h sync
\r
122 dw 00d06h ; vertical total
\r
123 dw 03e07h ; overflow (bit 8 of vertical counts)
\r
124 dw 04109h ; cell height (2 to double-scan)
\r
125 dw 0ea10h ; v sync start
\r
126 dw 0ac11h ; v sync end and protect cr0-cr7
\r
127 dw 0df12h ; vertical displayed
\r
128 dw 02d13h ; offset;
\r
129 dw 00014h ; turn off dword mode
\r
130 dw 0e715h ; v blank start
\r
131 dw 00616h ; v blank end
\r
132 dw 0e317h ; turn on byte mode
\r
136 X376Y282 label word
\r
139 dw 06e00h ; horz total
\r
140 dw 05d01h ; horz displayed
\r
141 dw 05e02h ; start horz blanking
\r
142 dw 09103h ; end horz blanking
\r
143 dw 06204h ; start h sync
\r
144 dw 08f05h ; end h sync
\r
145 dw 06206h ; vertical total
\r
146 dw 0f007h ; overflow
\r
147 dw 06109h ; cell height
\r
149 dw 03710h ; v sync start
\r
150 dw 08911h ; v sync end and protect cr0-cr7
\r
151 dw 03312h ; vertical displayed
\r
153 dw 00014h ; turn off dword mode
\r
154 dw 03c15h ; v blank start
\r
155 dw 05c16h ; v blank end
\r
156 dw 0e317h ; turn on byte mode
\r
160 LABEL X256Y400 word
\r
161 db 0e3h ; dot clock
\r
162 db 8 ; Number of CRTC Registers to update
\r
163 dw 05f00h ; horz total
\r
164 dw 03f01h ; horz displayed
\r
165 dw 04202h ; start horz blanking
\r
166 dw 09f03h ; end horz blanking
\r
167 dw 04c04h ; start h sync
\r
168 dw 00005h ; end h sync
\r
169 dw 04009h ; cell height
\r
170 dw 00014h ; turn off dword mode
\r
171 dw 0e317h ; turn on byte mode
\r
176 LABEL X256Y480 word
\r
177 db 0e3h ; dot clock
\r
178 db 16 ; Number of CRTC Registers to update
\r
179 dw 05f00h ; horz total
\r
180 dw 03f01h ; horz displayed
\r
181 dw 04202h ; start horz blanking
\r
182 dw 09f03h ; end horz blanking
\r
183 dw 04c04h ; start h sync
\r
184 dw 00005h ; end h sync
\r
185 dw 00d06h ; vertical total
\r
186 dw 03e07h ; overflow (bit 8 of vertical counts)
\r
187 dw 04009h ; cell height (2 to double-scan)
\r
188 dw 0ea10h ; v sync start
\r
189 dw 0ac11h ; v sync end and protect cr0-cr7
\r
190 dw 0df12h ; vertical displayed
\r
191 dw 00014h ; turn off dword mode
\r
192 dw 0e715h ; v blank start
\r
193 dw 00616h ; v blank end
\r
194 dw 0e317h ; turn on byte mode
\r
200 X320Y400 label word
\r
201 db 0e3h ; dot clock
\r
202 db 03 ; Number of CRTC Registers to update
\r
203 dw 04009h ; cell height
\r
204 dw 00014h ; turn off dword mode
\r
205 dw 0e317h ; turn on byte mode
\r
209 X320Y480 label word
\r
211 db 10 ; Number of CRTC Registers to update
\r
212 dw 00d06h ; vertical total
\r
213 dw 03e07h ; overflow (bit 8 of vertical counts)
\r
214 dw 04009h ; cell height (2 to double-scan)
\r
215 dw 0ea10h ; v sync start
\r
216 dw 0ac11h ; v sync end and protect cr0-cr7
\r
217 dw 0df12h ; vertical displayed
\r
218 dw 00014h ; turn off dword mode
\r
219 dw 0e715h ; v blank start
\r
220 dw 00616h ; v blank end
\r
221 dw 0e317h ; turn on byte mode
\r
225 X360Y400 label word
\r
226 db 0e7h ; dot clock
\r
227 db 09 ; Number of CRTC Registers to update
\r
228 dw 06b00h ; horz total
\r
229 dw 05901h ; horz displayed
\r
230 dw 05a02h ; start horz blanking
\r
231 dw 08e03h ; end horz blanking
\r
232 dw 05e04h ; start h sync
\r
233 dw 08a05h ; end h sync
\r
234 dw 04009h ; cell height
\r
235 dw 00014h ; turn off dword mode
\r
236 dw 0e317h ; turn on byte mode
\r
242 X360Y480 label word
\r
245 dw 06b00h ; horz total
\r
246 dw 05901h ; horz displayed
\r
247 dw 05a02h ; start horz blanking
\r
248 dw 08e03h ; end horz blanking
\r
249 dw 05e04h ; start h sync
\r
250 dw 08a05h ; end h sync
\r
251 dw 00d06h ; vertical total
\r
252 dw 03e07h ; overflow
\r
253 dw 04009h ; cell height
\r
254 dw 0ea10h ; v sync start
\r
255 dw 0ac11h ; v sync end and protect cr0-cr7
\r
256 dw 0df12h ; vertical displayed
\r
258 dw 00014h ; turn off dword mode
\r
259 dw 0e715h ; v blank start
\r
260 dw 00616h ; v blank end
\r
261 dw 0e317h ; turn on byte mode
\r
265 X360Y360 label word
\r
268 dw 06b00h ; horz total
\r
269 dw 05901h ; horz displayed
\r
270 dw 05a02h ; start horz blanking
\r
271 dw 08e03h ; end horz blanking
\r
272 dw 05e04h ; start h sync
\r
273 dw 08a05h ; end h sync
\r
274 dw 04009h ; cell height
\r
275 dw 08810h ; v sync start
\r
276 dw 08511h ; v sync end and protect cr0-cr7
\r
277 dw 06712h ; vertical displayed
\r
279 dw 00014h ; turn off dword mode
\r
280 dw 06d15h ; v blank start
\r
281 dw 0ba16h ; v blank end
\r
282 dw 0e317h ; turn on byte mode
\r
287 X376Y308 label word
\r
290 dw 06e00h ; horz total
\r
291 dw 05d01h ; horz displayed
\r
292 dw 05e02h ; start horz blanking
\r
293 dw 09103h ; end horz blanking
\r
294 dw 06204h ; start h sync
\r
295 dw 08f05h ; end h sync
\r
296 dw 06206h ; vertical total
\r
297 dw 00f07h ; overflow
\r
300 dw 03710h ; v sync start
\r
301 dw 08911h ; v sync end and protect cr0-cr7
\r
302 dw 03312h ; vertical displayed
\r
304 dw 00014h ; turn off dword mode
\r
305 dw 03c15h ; v blank start
\r
306 dw 05c16h ; v blank end
\r
307 dw 0e317h ; turn on byte mode
\r
311 X376Y564 label word
\r
314 dw 06e00h ; horz total
\r
315 dw 05d01h ; horz displayed
\r
316 dw 05e02h ; start horz blanking
\r
317 dw 09103h ; end horz blanking
\r
318 dw 06204h ; start h sync
\r
319 dw 08f05h ; end h sync
\r
320 dw 06206h ; vertical total
\r
321 dw 0f007h ; overflow
\r
324 dw 03710h ; v sync start
\r
325 dw 08911h ; v sync end and protect cr0-cr7
\r
326 dw 03312h ; vertical displayed
\r
328 dw 00014h ; turn off dword mode
\r
329 dw 03c15h ; v blank start
\r
330 dw 05c16h ; v blank end
\r
331 dw 0e317h ; turn on byte mode
\r
336 ModeTable label word ; Mode X tweak table
\r
355 _CurrXMode dw 0 ; Current graphics mode index
\r
356 _InGraphics db 0 ; Flag indicating graphics activity
\r
357 _ScrnPhysicalByteWidth dw 0 ; Physical width in bytes of screen
\r
358 _ScrnPhysicalPixelWidth dw 0 ; Physical width in pixels of screen
\r
359 _ScrnPhysicalHeight dw 0 ; Physical Height of screen
\r
360 _ErrorValue db 0 ; Set after function calls
\r
363 _SplitScrnActive db 0 ; Flag indicating Split scrn activity
\r
364 _DoubleBufferActive dw 0 ; Flag indicating double buffering
\r
365 _TrippleBufferActive dw 0 ; Flag indicating tripple buffering
\r
367 _SplitScrnScanLine dw 0 ; Split Screen's starting scan line
\r
368 _SplitScrnVisibleHeight dw 0 ; Split Screen's height on screen
\r
370 _SplitScrnOffs dw 0 ; Offset in video ram of Split Screen
\r
372 _Page0_Offs dw 0 ; Ofset in video ram of Main virtual
\r
373 ; screen ( = 0 if no split screen
\r
374 ; otherwise = offset of first byte
\r
375 ; after split screen
\r
376 _Page1_Offs dw 0 ; Ofset in video ram of Second virtual
\r
377 ; screen ( = 0 if no split screen
\r
378 ; otherwise = offset of first byte
\r
379 ; after split screen
\r
380 ; = Page0_Offs if Doubble buffering
\r
384 _NonVisual_Offs dw 0 ; Ofset in video ram of first byte
\r
385 ; of non visible ram
\r
386 _ScrnLogicalByteWidth dw 0 ; Logical width in bytes of screen
\r
387 _ScrnLogicalPixelWidth dw 0 ; Logical width in pixels of screen
\r
388 _ScrnLogicalHeight dw 0 ; Logical Height of screen
\r
390 _MaxScrollX dw 0 ; Max X start position of Physical
\r
391 ; screen within virtual screen (in
\r
393 _MaxScrollY dw 0 ; Max Y start position of Physical
\r
394 ; screen within virtual screen
\r
396 _VisiblePageIdx dw 0 ; Index of currently visible D.B.
\r
399 PageAddrTable label word
\r
400 _VisiblePageOffs dw 0 ; Table containing starting offsets
\r
401 _HiddenPageOffs dw 0 ; of the double buffer pages
\r
402 _WaitingPageOffs dw 0
\r
404 _TopClip dw 0 ; Clipping Rectangle
\r
406 _LeftClip dw 0 ; Left/Right coordinates in bytes
\r
408 _PhysicalStartByteX dw 0 ; X byte coord of physical screen
\r
409 ; relative to virtual virtual screen
\r
410 _PhysicalStartPixelX dw 0 ; X pixel coord of physical screen
\r
411 ; relative to virtual screen
\r
412 _PhysicalStartY dw 0 ; Y pixel coord of physical screen
\r
413 ; relative to virtual screen
\r
416 _VsyncHandlerActive dw 0
\r
417 _MouseRefreshFlag dw 0
\r
418 _MouseVsyncHandler dd 0
\r
419 _StartAddressFlag dw 0
\r
420 _WaitingStartLow dw 0
\r
421 _WaitingStartHigh dw 0
\r
422 _WaitingPelPan dw 0
\r
423 _VsyncPaletteStart dw 0
\r
424 _VsyncPaletteCount dw 0
\r
425 _VsyncPaletteBuffer label byte
\r
429 PARAMS_END label byte
\r
431 PARAM_COUNT equ ($-PARAMS)
\r
434 ; Index/data pairs for CRT Controller registers that differ between
\r
435 ; mode 13h and mode X.
\r
437 ;Pelpan values for 0,1,2,3 pixel panning to the left, respectively
\r
438 PelPanMask db 000h,002h,004h,006h
\r
440 DoubleScanFlag db ? ; Flag to indicate double scanned mode
\r
444 ;-------------------------------------------------------------------------
\r
445 ; Local Logical Screen Width setting function
\r
446 ; cx = Requitrd Logical Width
\r
448 ; WARNING: no registers are preserved
\r
450 SetLogicalScrWidth proc
\r
457 cmp ax,[_ScrnPhysicalPixelWidth]; Is logical width >= physical width
\r
458 jge @@ValidLogicalWidth ; yes - continue
\r
459 mov ax,bx ; no - set logical width = physical
\r
461 @@ValidLogicalWidth:
\r
465 ; The EXACT logical pixel width may not have been possible since
\r
466 ; it should be divisible by 8. Round down to the closest possible
\r
467 ; width and update the status variables
\r
471 mov [_ScrnLogicalByteWidth],ax ; Store the byte width of virtual
\r
472 mov [_RightClip],ax ; Set default Right clip column
\r
474 sub ax,[_ScrnPhysicalByteWidth] ; Calculate and store Max X position
\r
475 shl ax,2 ; of physical screen in virtual
\r
476 mov [_MaxScrollX],ax ; screen in pixels
\r
477 mov ax,bx ; set ax to byte width of virt scrn
\r
478 shl ax,2 ; convert to pixels
\r
479 mov [_ScrnLogicalPixelWidth],ax ; store virt scrn pixel width
\r
480 mov cx,ax ; save ax (return value)
\r
482 ; calculate no. non split screen rows in video ram
\r
484 mov ax,0ffffh ; cx = Maximum video ram offset
\r
485 sub dx,dx ; DX:AX is divide operand, set DX = 0
\r
486 div bx ; divide ax by ScrnLogicalByteWidth
\r
487 mov [_ScrnLogicalHeight],ax ; Save Screen Logical Height
\r
488 mov [_BottomClip],ax ; Set default bottom clip row
\r
489 sub ax,[_ScrnPhysicalHeight] ; Update the maximum Y position of
\r
490 mov [_MaxScrollY],ax ; Physical screen in logical screen
\r
491 mov ax,cx ; restore ax (return value)
\r
493 ; calculate initial NonVisual
\r
494 mov ax,[_ScrnLogicalByteWidth]
\r
495 mul [_ScrnPhysicalHeight]
\r
496 mov [_NonVisual_Offs],ax
\r
499 SetLogicalScrWidth endp
\r
505 out dx,ax ; enable writes to all four planes
\r
506 mov ax,SCREEN_SEG ; now clear all display memory, 8 pixels
\r
507 mov es,ax ; at a time
\r
508 sub di,di ; point ES:DI to display memory
\r
512 sub ax,ax ; clear to zero-value pixels
\r
513 mov cx,0FFFFh ; # of words in display memory
\r
514 rep stosw ; clear all of display memory
\r
521 ;-----------------------------------------------------------------------
\r
522 ; Mode X graphics mode set with a virtual screen
\r
523 ; logical screen width.
\r
524 ; C near-callable as:
\r
526 ; int x_set_mode(unsigned int mode,unsigned int WidthInPixels);
\r
528 ; returns the actual width of the allocated virtual screen in pixels
\r
529 ; if a valid mode was selected otherwise returns -1
\r
531 ; Saves virtual screen pixel width in _ScrnLogicalPixelWidth.
\r
532 ; Saves virtual screen byte width in _ScrnLogicalByteWidth.
\r
533 ; Physical screen dimensions are set in _ScrnPhysicalPixelWidth,
\r
534 ; _ScrnPhysicalByteWidth and _ScrnPhysicalHeight
\r
537 ; Modes: 0 = 320 x 200 (256 color) NOTE: Some of these modes require
\r
538 ; 1 = 320 x 240 (256 color) vertical size adjustment.
\r
539 ; 2 = 360 x 200 (256 color)
\r
540 ; 3 = 360 x 240 (256 color)
\r
541 ; 4 = 320 x 400 (256 color)
\r
542 ; 5 = 320 x 480 (256 color)
\r
543 ; 6 = 360 x 200 (256 color)
\r
544 ; 7 = 360 x 480 (256 color)
\r
545 ; 8 = 360 x 360 (256 color)
\r
546 ; 9 = 376 x 308 (256 color)
\r
547 ; 10 = 376 x 564 (256 color)
\r
549 ; Written by Themie Gouthas,
\r
550 ; parts adapted from M. Abrash code.
\r
551 ;------------------------------------------------------------------------
\r
553 ARG mode:word,logicalscrwidth:word
\r
554 push bp ;preserve caller's stack frame
\r
557 push si ;preserve C register vars
\r
558 push di ; (don't count on BIOS preserving anything)
\r
563 mov di,offset PARAMS
\r
569 cmp cx,LAST_X_MODE ; have we selected a valid mode
\r
570 jle @@ValidMode ; Yes !
\r
572 mov [_InGraphics],FALSE ; No return -1
\r
581 mov [_CurrXMode],cx
\r
582 mov [_InGraphics],TRUE
\r
586 jg @@SetDoubleScanFlag
\r
588 @@SetDoubleScanFlag:
\r
589 mov [DoubleScanFlag],al
\r
590 push cx ; some bios's dont preserve cx
\r
594 mov ax,13h ; let the BIOS set standard 256-color
\r
595 int 10h ; mode (320x200 linear)
\r
602 out dx,ax ; disable chain4 mode
\r
604 out dx,ax ; synchronous reset while setting Misc
\r
605 ; Output for safety, even though clock
\r
608 mov bx,offset ModeTable
\r
611 mov si, word ptr [bx]
\r
617 out dx,al ; select the dot clock and Horiz
\r
622 out dx,ax ; undo reset (restart sequencer)
\r
625 mov dx,CRTC_INDEX ; reprogram the CRT Controller
\r
626 mov al,11h ; VSync End reg contains register write
\r
627 out dx,al ; protect bit
\r
628 inc dx ; CRT Controller Data register
\r
629 in al,dx ; get current VSync End register setting
\r
630 and al,07fh ; remove write protect on various
\r
631 out dx,al ; CRTC registers
\r
632 dec dx ; CRT Controller Index
\r
639 lodsw ; get the next CRT Index/Data pair
\r
640 out dx,ax ; set the next CRT Index/Data pair
\r
641 loop @@SetCRTParmsLoop
\r
645 out dx,ax ; enable writes to all four planes
\r
646 mov ax,SCREEN_SEG ; now clear all display memory, 8 pixels
\r
647 mov es,ax ; at a time
\r
648 sub di,di ; point ES:DI to display memory
\r
649 sub ax,ax ; clear to zero-value pixels
\r
650 mov cx,8000h ; # of words in display memory
\r
651 rep stosw ; clear all of display memory
\r
653 ; Set pysical screen dimensions
\r
655 lodsw ; Load scrn pixel width
\r
656 mov [_ScrnPhysicalPixelWidth],ax ; from tweak table and store
\r
657 mov [_SplitScrnScanLine],ax ; No splitscrn ==
\r
658 ; splitscrn=PhysicalscrnHeight
\r
659 mov bx,ax ; Copy width for later use
\r
660 shr ax,2 ; Convert to byte width
\r
661 mov [_ScrnPhysicalByteWidth],ax ; Store for later use
\r
662 lodsw ; Load Screen Phys. Height
\r
663 mov [_ScrnPhysicalHeight],ax ; Store for later use
\r
666 ; Mode X is set, now set the required logical page width.
\r
668 mov cx,[logicalscrwidth]
\r
670 call SetLogicalScrWidth
\r
672 pop di ;restore C register vars
\r
674 pop bp ;restore caller's stack frame
\r
678 ;----------------------------------------------------------------------
\r
679 ; Mode X (256 color mode) set default access video plane
\r
681 ; C near-callable as:
\r
682 ; void x_select_default_plane(unsigned char plane);
\r
684 ; Enables Read/Write access to a plane using general memory access
\r
687 ; Written by Themie Gouthas
\r
688 ;----------------------------------------------------------------------
\r
689 _x_select_default_plane proc
\r
692 mov bp,sp ; set up stack frame
\r
693 mov cl,byte ptr [Plane]
\r
695 ; SELECT WRITE PLANE
\r
696 and cl,011b ;CL = plane
\r
697 mov ax,0100h + MAP_MASK ;AL = index in SC of Map Mask reg
\r
698 shl ah,cl ;set only the bit for the required
\r
700 mov dx,SC_INDEX ;set the Map Mask to enable only the
\r
701 out dx,ax ; pixel's plane
\r
703 ; SELECT READ PLANE
\r
704 mov ah,cl ;AH = plane
\r
705 mov al,READ_MAP ;AL = index in GC of the Read Map reg
\r
706 mov dx,GC_INDEX ;set the Read Map to read the pixel's
\r
711 _x_select_default_plane endp
\r
714 ;----------------------------------------------------------------------
\r
715 ; Mode X (256 color mode) Set Mode X split screen starting row
\r
716 ; The split screen resides on the bottom half of the screen and has a
\r
717 ; starting address of A000:0000
\r
719 ; C near-callable as:
\r
720 ; void x_set_splitscreen(unsigned int line);
\r
722 ; Updates _Page0_Offs to reflect the existence of the split screen region
\r
723 ; ie _MainScrnOffset is set to the offset of the first pixel beyond the split
\r
726 ; Written by Themie Gouthas
\r
727 ;----------------------------------------------------------------------
\r
729 _x_set_splitscreen proc
\r
732 mov bp,sp ; set up stack frame
\r
735 xor si,si ; si=0 -> x virtual page start coord
\r
737 cmp [_DoubleBufferActive],0
\r
740 cmp [_SplitScrnActive],0
\r
741 je @@NotPreviouslyCalled
\r
744 mov [_ErrorValue],ERROR
\r
746 pop bp ; Return if previously called
\r
749 @@NotPreviouslyCalled:
\r
751 ; Turn on split screen pal pen suppression, so the split screen
\r
752 ; wo'nt be subject to pel panning as is the non split screen portion.
\r
754 mov dx,INPUT_STATUS_0
\r
755 in al,dx ; Reset the AC Index/Data toggle to
\r
757 mov al,AC_MODE_CONTROL+20h ; Bit 5 set to prevent screen blanking
\r
758 mov dx,AC_INDEX ; Point AC to Index/Data register
\r
760 inc dx ; Point to AC Data reg (for reads only)
\r
761 in al,dx ; Get the current AC Mode Control reg
\r
762 or al,20h ; Enable split scrn Pel panning suppress.
\r
763 dec dx ; Point to AC Index/Data reg (for writes only)
\r
764 out dx,al ; Write the new AC Mode Control setting
\r
765 ; with split screen pel panning
\r
766 ; suppression turned on
\r
768 mov [_PhysicalStartByteX],ax ; Set the Phisical screen start
\r
769 mov [_PhysicalStartPixelX],ax ; offset within virtual screen
\r
770 mov [_PhysicalStartY],ax
\r
771 mov [_SplitScrnActive],TRUE
\r
773 jns @@NotNeg ; Check that Split Scrn start scan line is +ve
\r
775 mov ax,0 ; Since -ve set to 0
\r
778 mov [_SplitScrnScanLine],ax ; save the scanline
\r
782 or [DoubleScanFlag],0
\r
783 jz @@NotDoubleScanned
\r
786 @@NotDoubleScanned:
\r
787 ;mov cl,[DoubleScanFlag]
\r
788 ;shl ax,cl ; Mode X 200 and 240 line modes are actually
\r
789 ; 400 and 480 lines that are double scanned
\r
790 ; so for start scanline multiply required ModeX
\r
791 ; scan line by 2 if its a double scanned mode
\r
794 mov bx,ax ; save the scanline
\r
797 WaitVsyncStart ; wait for vertical retrace
\r
799 cli ; Dont allow register setting to be interrupted
\r
802 mov al,LINE_COMPARE
\r
803 out dx,ax ; Bits 7-0 of the split screen scan line
\r
808 mov al,OVERFLOW ; Bit 4 of overflow register = Bit 8 of split
\r
809 out dx,al ; screen scan line,
\r
810 inc dx ; So using readability of VGA registers
\r
811 in al,dx ; Read the OVERFLOW register, and set the
\r
812 and al, not 10h ; bit corresponding to Bit 8 (above)
\r
820 mov al,MAX_SCAN_LINE ; Bit 6 of max scan line register =
\r
821 out dx,al ; Bit 9 of split screen scan line
\r
822 inc dx ; As we did before, update the apropriate
\r
823 in al,dx ; bit without disturbing the rest
\r
827 sti ; Registers are set, so interrupts are safe
\r
829 mov ax,[_ScrnPhysicalHeight] ; Determine where the first byte
\r
830 sub ax,[_SplitScrnScanLine] ; of the non split screen video ram
\r
831 mov [_SplitScrnVisibleHeight],ax ; starts and store it for reference
\r
833 mov bx,[_ScrnLogicalByteWidth]
\r
835 mov [_Page0_Offs],ax
\r
836 mov [_Page1_Offs],ax
\r
837 mov [_Page2_Offs],ax
\r
839 ; calculate no. non split screen rows in video ram
\r
840 mov cx,0ffffh ; cx = Maximum video ram offset
\r
841 sub cx,ax ; cx = cx - _Page0_Offs
\r
842 xchg cx,ax ; swap cx and ax
\r
843 sub dx,dx ; DX:AX is divide operand, set DX = 0
\r
844 div bx ; divide ax (prev cx) by
\r
845 ; ScrnLogicalByteWidth
\r
847 mov [_ScrnLogicalHeight],ax ; Save Screen Logical Height
\r
848 cmp ax,[_BottomClip]
\r
849 jle @@BottomClipOK ; Adjust Clip Rectangle if necessary
\r
850 mov [_BottomClip],ax
\r
852 sub ax,[_SplitScrnScanLine] ; Update the maximum Y position of
\r
853 mov [_MaxScrollY],ax ; Physical screen in logical screen
\r
855 xchg cx,ax ; restore original ax (MainScrnOfs)
\r
856 mov bh,al ; Set the visible screen start address
\r
857 mov ch,ah ; to the top left corner of the virtual
\r
858 jmp StartAddrEntry ; screen
\r
859 _x_set_splitscreen endp
\r
862 ;-----------------------------------------------------------------------
\r
863 ; Mode X (256 color mode) Page flip primer
\r
864 ; No clipping is performed.
\r
865 ; C near-callable as:
\r
867 ; void x_page_flip(unsigned int x, unsigned int y);
\r
869 ; Swaps visible and hidden page offsets and then executes the SetStartAddr
\r
870 ; to achieve a page flip.
\r
872 ; SEE x_set_start_addr below
\r
874 ; Written by Themie Gouthas
\r
875 ;------------------------------------------------------------------------
\r
879 push bp ;preserve caller's stack frame
\r
880 mov bp,sp ;point to local stack frame
\r
884 mov ax,[_ScrnLogicalByteWidth] ; Calculate Offset increment
\r
887 cmp [_DoubleBufferActive],TRUE ; Do we have double buffering ?
\r
889 cmp [_TrippleBufferActive],TRUE
\r
893 mov bx,[_HiddenPageOffs]
\r
894 xchg bx,[_VisiblePageOffs]
\r
895 xchg bx,[_WaitingPageOffs]
\r
896 mov [_HiddenPageOffs],bx
\r
897 mov bx,[_VisiblePageIdx]
\r
903 mov [_VisiblePageIdx],bx
\r
904 jmp short PageFlipEntry2
\r
906 mov bx,[_HiddenPageOffs]
\r
907 xchg bx,[_VisiblePageOffs] ; Swap the Page Offsete
\r
908 xchg [_HiddenPageOffs],bx
\r
909 xor [_VisiblePageIdx],01h ; Set the Visible page index
\r
910 jmp short PageFlipEntry2
\r
914 ;-----------------------------------------------------------------------
\r
915 ; Mode X (256 color mode) Set Mode X non split screen start address
\r
916 ; of logical screen.
\r
917 ; C near-callable as:
\r
919 ; void x_set_start_addr(unsigned int x, unsigned int y);
\r
921 ; Params: StartOffset is offset of first byte of logical screen ram
\r
922 ; (Useful if you want to double buffer by splitting your non
\r
923 ; split screen video ram into 2 pages)
\r
924 ; X,Y coordinates of the top left hand corner of the physical screen
\r
925 ; within the logical screen
\r
926 ; X must not exceed (Logical screen width - Physical screen width)
\r
927 ; Y must not exceed (Logical screen height - Physical screen height)
\r
930 ; Written by Themie Gouthas,
\r
931 ; Parts addapted from M. Abrash code published in DDJ Mag.
\r
932 ;------------------------------------------------------------------------
\r
933 _x_set_start_addr proc
\r
940 mov ax,[_ScrnLogicalByteWidth] ; Calculate Offset increment
\r
943 cmp [_DoubleBufferActive],TRUE ; Do we have double buffering ?
\r
944 je @@PageResolution
\r
945 cmp [_TrippleBufferActive],TRUE
\r
946 je @@PageResolution
\r
948 add ax,[_Page0_Offs] ; no - add page 0 offset
\r
949 jmp short @@AddColumn
\r
953 mov [_PhysicalStartPixelX],si
\r
954 mov [_PhysicalStartY],cx
\r
957 add ax,[_VisiblePageOffs] ; Add visible page offset
\r
962 mov [_PhysicalStartByteX],cx
\r
963 add ax,cx ; add the column offset for X
\r
964 mov bh,al ; setup CRTC start addr regs and
\r
965 ; values in word registers for
\r
966 mov ch,ah ; fast word outs
\r
971 and si,0003h ; select pel pan register value for the
\r
972 mov ah,PelPanMask[si] ; required x coordinate
\r
973 mov al,PEL_PANNING+20h
\r
976 cmp [_VsyncHandlerActive],TRUE
\r
977 jne @@NoVsyncHandler
\r
980 cmp [_StartAddressFlag],0
\r
983 mov [_WaitingStartLow],bx
\r
984 mov [_WaitingStartHigh],cx
\r
985 mov [_WaitingPelPan],si
\r
986 mov [_StartAddressFlag],1
\r
991 mov dx,INPUT_STATUS_0 ;Wait for trailing edge of Vsync pulse
\r
995 jnz @@WaitDE ;display enable is active low (0 = active)
\r
1000 out dx,ax ;start address low
\r
1002 out dx,ax ;start address high
\r
1005 ; Now wait for vertical sync, so the other page will be invisible when
\r
1006 ; we start drawing to it.
\r
1007 mov dx,INPUT_STATUS_0 ;Wait for trailing edge of Vsync pulse
\r
1011 jz @@WaitVS ;display enable is active low (0 = active)
\r
1015 mov ax,si ; Point the attribute controller to pel pan
\r
1017 out dx,al ; reg. Bit 5 also set to prevent blanking
\r
1019 out dx,al ; load new Pel Pan setting.
\r
1023 mov [_ErrorValue],OK
\r
1027 _x_set_start_addr endp
\r
1030 ;-----------------------------------------------------------------------
\r
1031 ; Mode X (256 color mode) Mode X split screen hide
\r
1032 ; C near-callable as:
\r
1034 ; void x_hide_splitscreen()
\r
1036 ; Hides an existing split screen by setting its starting scan line to
\r
1037 ; the last physical screen scan line
\r
1039 ; WARNING: Only to be used if SplitScrnLine has been previously called
\r
1040 ; WARNING: DO NOT USE with mode 5-11 (320x400-376x564). The memory for
\r
1041 ; the initial split screen is reserved and the size limitations
\r
1042 ; of these modes means any change in the split screen scan line
\r
1043 ; will encroach on the split screen ram
\r
1045 ; Written by Themie Gouthas
\r
1046 ;------------------------------------------------------------------------
\r
1048 _x_hide_splitscreen proc
\r
1052 cmp [_SplitScrnActive],TRUE
\r
1053 je @@SplitScreenEnabled
\r
1056 mov [_ErrorValue],ERROR
\r
1060 @@SplitScreenEnabled:
\r
1061 cmp [_CurrXMode],4 ; Do nothing for Modes > 2
\r
1063 mov bx,[_ScrnPhysicalHeight]
\r
1065 mov ax,[_ScrnLogicalHeight]
\r
1067 mov [_MaxScrollY],ax
\r
1069 mov [_SplitScrnVisibleHeight],ax
\r
1071 or [DoubleScanFlag],0
\r
1072 jz @@NotDoubleScanned
\r
1075 @@NotDoubleScanned:
\r
1076 ;mov cl,[DoubleScanFlag] ; Compensate for double scanned modes
\r
1079 WaitVsyncStart ; wait for vertical retrace
\r
1081 cli ; Dont allow register setting to be interrupted
\r
1084 mov al,LINE_COMPARE
\r
1085 out dx,ax ; Bits 7-0 of the split screen scan line
\r
1090 mov al,OVERFLOW ; Bit 4 of overflow register = Bit 8 of split
\r
1091 out dx,al ; screen scan line,
\r
1092 inc dx ; So using readability of VGA registers
\r
1093 in al,dx ; Read the OVERFLOW register, and set the
\r
1094 and al, not 10h ; bit corresponding to Bit 8 (above)
\r
1102 mov al,MAX_SCAN_LINE ; Bit 6 of max scan line register =
\r
1103 out dx,al ; Bit 9 of split screen scan line
\r
1104 inc dx ; As we did before, update the apropriate
\r
1105 in al,dx ; bit without disturbing the rest
\r
1109 sti ; Registers are set, so interrupts are safe
\r
1113 mov [_ErrorValue],OK
\r
1116 _x_hide_splitscreen endp
\r
1118 ;-----------------------------------------------------------------------
\r
1119 ; Mode X (256 color mode) Mode X split screen show
\r
1120 ; C near-callable as:
\r
1122 ; void x_show_splitscreen()
\r
1124 ; Restores split screen start scan line to the initial split screen
\r
1125 ; starting scan line as set by SplitScrnLine.
\r
1127 ; WARNING: Only to be used if SplitScrnLine has been previously called
\r
1128 ; WARNING: DO NOT USE with mode 5-11 (320x400-376x564). The memory for
\r
1129 ; the initial split screen is reserved and the size limitations
\r
1130 ; of these modes means any change in the split screen scan line
\r
1131 ; will encroach on the split screen ram
\r
1132 ; Update: Now disabled for these modes
\r
1134 ; Written by Themie Gouthas
\r
1135 ;------------------------------------------------------------------------
\r
1138 _x_show_splitscreen proc
\r
1142 cmp [_SplitScrnActive],TRUE
\r
1143 je @@SplitScreenEnabled
\r
1146 mov [_ErrorValue],ERROR
\r
1150 @@SplitScreenEnabled:
\r
1151 cmp [_CurrXMode],4 ; Do nothing for Modes > 2
\r
1154 mov bx,[_SplitScrnScanLine]
\r
1155 mov ax,[_ScrnLogicalHeight] ; Update Max Scroll Y
\r
1157 mov [_MaxScrollY],ax
\r
1159 mov ax,[_ScrnPhysicalHeight]
\r
1161 mov [_SplitScrnVisibleHeight],ax
\r
1163 or [DoubleScanFlag],0
\r
1164 jz @@NotDoubleScanned
\r
1167 @@NotDoubleScanned:
\r
1168 ;mov cl,[DoubleScanFlag] ; Compensate for double scanned modes
\r
1170 WaitVsyncStart ; wait for vertical retrace
\r
1172 cli ; Dont allow register setting to be interrupted
\r
1175 mov al,LINE_COMPARE
\r
1176 out dx,ax ; Bits 7-0 of the split screen scan line
\r
1181 mov al,OVERFLOW ; Bit 4 of overflow register = Bit 8 of split
\r
1182 out dx,al ; screen scan line,
\r
1183 inc dx ; So using readability of VGA registers
\r
1184 in al,dx ; Read the OVERFLOW register, and set the
\r
1185 and al, not 10h ; bit corresponding to Bit 8 (above)
\r
1193 mov al,MAX_SCAN_LINE ; Bit 6 of max scan line register =
\r
1194 out dx,al ; Bit 9 of split screen scan line
\r
1195 inc dx ; As we did before, update the apropriate
\r
1196 in al,dx ; bit without disturbing the rest
\r
1200 sti ; Registers are set, so interrupts are safe
\r
1203 mov [_ErrorValue],0
\r
1206 _x_show_splitscreen endp
\r
1209 ;-----------------------------------------------------------------------
\r
1210 ; Mode X (256 color mode) Modify Mode X split screen starting scan line
\r
1211 ; C near-callable as:
\r
1213 ; void x_adjust_splitscreen(unsigned int ScanLine)
\r
1215 ; Sets the split screen start scan line to a new scan line. Valid scan lines
\r
1216 ; are between the initial split screen starting scan line and the last
\r
1217 ; physical screen scan line.
\r
1219 ; WARNING: Only to be used if SplitScrnLine has been previously called
\r
1220 ; WARNING: DO NOT USE with mode 5-11 (320x400-376x564). The memory for
\r
1221 ; the initial split screen is reserved and the size limitations
\r
1222 ; of these modes means any change in the split screen scan line
\r
1223 ; will encroach on the split screen ram
\r
1224 ; Update: Now disabled for these modes
\r
1227 ; Written by Themie Gouthas
\r
1228 ;------------------------------------------------------------------------
\r
1231 _x_adjust_splitscreen proc
\r
1236 cmp [_SplitScrnActive],TRUE
\r
1237 je @@SplitScreenEnabled
\r
1240 mov [_ErrorValue],ERROR
\r
1244 @@SplitScreenEnabled:
\r
1245 cmp [_CurrXMode],4 ; Do nothing for Modes > 2
\r
1247 mov bx,[ScanLine] ; Is the required starting scan line
\r
1248 cmp bx,[_SplitScrnScanLine] ; valid ?
\r
1249 js @@Done ; No - Then do nothing
\r
1253 mov ax,[_ScrnLogicalHeight] ; Update Max Scroll Y
\r
1255 mov [_MaxScrollY],ax
\r
1257 mov ax,[_ScrnPhysicalHeight]
\r
1259 mov [_SplitScrnVisibleHeight],ax
\r
1261 or [DoubleScanFlag],0
\r
1262 jz @@NotDoubleScanned
\r
1265 @@NotDoubleScanned:
\r
1266 ;mov cl,[DoubleScanFlag] ; Compensate for double scanned modes
\r
1269 WaitVsyncStart ; wait for vertical retrace
\r
1271 cli ; Dont allow register setting to be interrupted
\r
1275 mov al,LINE_COMPARE
\r
1276 out dx,ax ; Bits 7-0 of the split screen scan line
\r
1281 mov al,OVERFLOW ; Bit 4 of overflow register = Bit 8 of split
\r
1282 out dx,al ; screen scan line,
\r
1283 inc dx ; So using readability of VGA registers
\r
1284 in al,dx ; Read the OVERFLOW register, and set the
\r
1285 and al, not 10h ; bit corresponding to Bit 8 (above)
\r
1293 mov al,MAX_SCAN_LINE ; Bit 6 of max scan line register =
\r
1294 out dx,al ; Bit 9 of split screen scan line
\r
1295 inc dx ; As we did before, update the apropriate
\r
1296 in al,dx ; bit without disturbing the rest
\r
1300 sti ; Registers are set, so interrupts are safe
\r
1302 mov [_ErrorValue],OK
\r
1305 _x_adjust_splitscreen endp
\r
1309 ;-----------------------------------------------------------------------
\r
1310 ; Mode X (256 color mode) Enable DoubleBuffering on non split screen area
\r
1311 ; C near-callable as:
\r
1313 ; int x_set_doublebuffer(unsigned int PageHeight);
\r
1315 ; Params: PageHeight is the height of the virtual screen to double buffer
\r
1316 ; Returns the closest possible height to the specified.
\r
1318 ; Sets up two double buffering virtual pages
\r
1319 ; GLOBAL variables set:
\r
1321 ; _Page1_Offs Offset of second virtual page
\r
1322 ; _NonVisual_Offs Offset of first non visible video ram byte
\r
1323 ; _DoubleBufferActive Flag
\r
1324 ; _PageAddrTable Table of Double buffering pages start offsets
\r
1325 ; _ScrnLogicalHeight Logical height of the double buffering pages
\r
1328 ; Written by Themie Gouthas
\r
1329 ;------------------------------------------------------------------------
\r
1332 _x_set_doublebuffer proc
\r
1333 ARG PageHeight:word
\r
1337 cmp [_DoubleBufferActive],0
\r
1340 mov [_ErrorValue],ERROR
\r
1345 mov [_VisiblePageIdx],0 ; Set visible Page to 0
\r
1346 mov ax,[_ScrnLogicalHeight] ; Set Maximum D.B. Page height to
\r
1347 shr ax,1 ; _ScrnLogicalHeight / 2
\r
1349 mov bx,[PageHeight] ; Is the require D.B. Page Height
\r
1350 cmp ax,bx ; > the Maximum D.B. Page Height ?
\r
1352 js @@InvalidHeight ; no - jump
\r
1353 mov ax,bx ; yes - Set the D.B. Page height to
\r
1354 ; to the maximum allowed.
\r
1357 mov [_ScrnLogicalHeight],ax ; Update logical screen height to
\r
1358 ; reflect the height of a D.B. page
\r
1359 cmp ax,[_BottomClip]
\r
1360 jle @@BottomClipOK ; Adjust Clip Rectangle if necessary
\r
1361 mov [_BottomClip],ax
\r
1364 mul [_ScrnLogicalByteWidth] ; Calculate the offset of the second
\r
1365 mov cx,ax ; D.B. Page in video ram
\r
1366 mov bx,[_Page0_Offs]
\r
1367 mov [_VisiblePageOffs],bx
\r
1370 mov [_Page1_Offs],ax ; Save it
\r
1371 mov [_HiddenPageOffs],ax
\r
1373 add ax,cx ; Calculate the offset of first byte
\r
1374 mov [_NonVisual_Offs],ax ; beyond the D.B. pages and save it
\r
1375 mov [_DoubleBufferActive],TRUE ; Set flag indicating D.B'ing mode on
\r
1378 sub ax,[_ScrnPhysicalHeight]
\r
1379 add ax,[_SplitScrnVisibleHeight]
\r
1380 mov [_MaxScrollY],ax
\r
1382 mov ax,dx ; return the D.B. pages' height
\r
1383 mov [_ErrorValue],OK
\r
1386 _x_set_doublebuffer endp
\r
1389 ;-----------------------------------------------------------------------
\r
1390 ; Mode X (256 color mode) Enable TrippleBuffering on non split screen area
\r
1391 ; C near-callable as:
\r
1393 ; int x_set_tripplebuffer(unsigned int PageHeight);
\r
1395 ; Params: PageHeight is the height of the virtual screen to tripple buffer
\r
1396 ; Returns the closest possible height to the specified.
\r
1398 ; Sets up two tripple buffering virtual pages
\r
1399 ; GLOBAL variables set:
\r
1401 ; _Page1_Offs Offset of second virtual page
\r
1402 ; _Page2_Offs Offset of third virtual page
\r
1403 ; _NonVisual_Offs Offset of first non visible video ram byte
\r
1404 ; _DoubleBufferActive Flag
\r
1405 ; _PageAddrTable Table of Double buffering pages start offsets
\r
1406 ; _ScrnLogicalHeight Logical height of the double buffering pages
\r
1409 ; Almost written by Tore Bastiansen (cut & paste from _x_set_doublebuffer)
\r
1410 ;------------------------------------------------------------------------
\r
1412 _x_set_tripplebuffer proc
\r
1413 ARG PageHeight:word
\r
1417 cmp [_DoubleBufferActive],0
\r
1419 cmp [_TrippleBufferActive],0
\r
1422 mov [_ErrorValue],ERROR
\r
1427 mov [_VisiblePageIdx],0 ; Set visible Page to 0
\r
1428 mov ax,[_ScrnLogicalHeight] ; Set Maximum T.B. Page height to
\r
1431 idiv bx ; _ScrnLogicalHeight / 3
\r
1433 mov bx,[PageHeight] ; Is the require T.B. Page Height
\r
1434 cmp ax,bx ; > the Maximum T.B. Page Height ?
\r
1436 js @@InvalidHeight ; no - jump
\r
1437 mov ax,bx ; yes - Set the T.B. Page height to
\r
1438 ; to the maximum allowed.
\r
1441 mov [_ScrnLogicalHeight],ax ; Update logical screen height to
\r
1442 ; reflect the height of a T.B. page
\r
1443 cmp ax,[_BottomClip]
\r
1444 jle @@BottomClipOK ; Adjust Clip Rectangle if necessary
\r
1445 mov [_BottomClip],ax
\r
1448 mul [_ScrnLogicalByteWidth] ; Calculate the offset of the second
\r
1449 mov cx,ax ; D.B. Page in video ram
\r
1450 mov bx,[_Page0_Offs]
\r
1451 mov [_VisiblePageOffs],bx
\r
1454 mov [_Page1_Offs],ax ; Save it
\r
1455 mov [_HiddenPageOffs],ax
\r
1458 mov [_Page2_Offs],ax ; Save the other it ?
\r
1459 mov [_WaitingPageOffs],ax
\r
1461 add ax,cx ; Calculate the offset of first byte
\r
1462 mov [_NonVisual_Offs],ax ; beyond the D.B. pages and save it
\r
1463 mov [_TrippleBufferActive],TRUE ; Set flag indicating T.B'ing mode on
\r
1466 sub ax,[_ScrnPhysicalHeight]
\r
1467 add ax,[_SplitScrnVisibleHeight]
\r
1468 mov [_MaxScrollY],ax
\r
1470 mov ax,dx ; return the D.B. pages' height
\r
1471 mov [_ErrorValue],OK
\r
1474 _x_set_tripplebuffer endp
\r
1477 ;-----------------------------------------------------------------------
\r
1478 ; Set Clipping rectangle
\r
1482 ; int x_set_cliprect(WORD left,WORD top, WORD right, WORD bottom);
\r
1485 ; NOTE clipping is byte oriented. "left" and "right" are in bytes not pixels.
\r
1486 ; Only selected functions perform any clipping at all.
\r
1488 ; Written by Themie Gouthas
\r
1489 ;------------------------------------------------------------------------
\r
1491 _x_set_cliprect proc
\r
1492 ARG left:word,top:word,right:word,bottom:word
\r
1498 jns @@CorrectXOrder
\r
1501 mov [_LeftClip],ax
\r
1502 mov [_RightClip],bx
\r
1506 jns @@CorrectYOrder
\r
1510 mov [_BottomClip],bx
\r
1513 _x_set_cliprect endp
\r
1516 ;----------------------------------------------------------------------
\r
1517 ; Return to text mode
\r
1522 mov ax,03h ; Restore Text Mode
\r
1529 ;-----------------------------------------------------------------------
\r
1530 ; Wait for Vertical sync
\r
1531 _x_wait_vsync proc
\r
1536 _x_wait_vsync endp
\r