.MODEL HUGE, C

.486

.CODE

; XMODE routines by Kaos (a.k.a. Bruno Carlos) of Black Magic

; Mode 0 -> 320x200 4 pages
; Mode 1 -> 320x240 3 pages
; Mode 2 -> 320x400 2 pages
; Mode 3 -> 320x480 1 page
; Mode 4 -> 360x200 3 pages
; Mode 5 -> 360x240 3 pages
; Mode 6 -> 360x400 1 page
; Mode 7 -> 360x480 1 page

; If you use this code in any of your
; productions give us the proper credits...

;****************************************************************************

out_16  MACRO register, value

	ifdifi <register>,<dx>          ; if dx not setup
	mov dx,register                 ; then select register
	endif
	ifdifi <value>,<ax>             ; if ax not setup
	mov ax,value                    ; then get data value
	endif
	out dx,ax                       ; set i/o register(s)
ENDM

;****************************************************************************

out_8   MACRO register, value

	ifdifi <register>,<dx>          ; if dx not setup
	mov dx,register                 ; then select register
	endif
	ifdifi <value>,<al>             ; if al not setup
	mov al,value                    ; then get data value
	endif
	out dx,al                       ; set i/o register
ENDM

;******************************************************************************

vwait 	MACRO
	local	Vertout,Vertin

	mov   	dx,03DAh
 Vertout:
	in    	al,dx
	test  	al,8
	jnz   	Vertout  	; wait Until out of vertical retrace
 Vertin:
	in    	al,dx
	test  	al,8
	jz    	Vertin   	; wait Until inside vertical retrace
ENDM

;******************************************************************************

hwait 	MACRO
	local	Horzout,Horzin

	mov   	dx,03DAh
 Horzout:
	in    	al,dx
	test  	al,1
	jnz   	Horzout  	; wait Until out of horizontal retrace
 Horzin:
	in    	al,dx
	test  	al,1
	jz    	Horzin   	; wait Until inside horizontal retrace
ENDM

;****************************************************************************

xmode:
	dw  offset X320x200
	dw  offset X320x240
	dw  offset X320x400
	dw  offset X320x480
	dw  offset X360x200
	dw  offset X360x240
	dw  offset X360x400
	dw  offset X360x480

;****************************************************************************

X320x200:

	db  063h        ; 400 scan lines & 25 mhz clock
	dw  05f00h      ; horz total
	dw  04f01h      ; horz displayed
	dw  05002h      ; start horz blanking
	dw  08203h      ; end horz blanking
	dw  05404h      ; start h sync
	dw  08005h      ; end h sync
	dw  0bf06h      ; vertical total
	dw  01f07h      ; overflow
	dw  09c10h      ; v sync start
	dw  08e11h      ; v sync end/prot cr0 cr7
	dw  08f12h      ; vertical displayed
	dw  09615h      ; v blank start
	dw  0b916h      ; v blank end
	dw  04109h      ; cell height (2 scan lines)
	dw  00014h      ; dword mode off
	dw  0e317h      ; turn on byte mode
	dw  16000/4     ; page size (dwords)
	dw  320         ; xmax
	dw  200         ; ymax
	dw  0		; page 1 offset
	dw  16000	; page 2 offset
	dw  32000	; page 3 offset
	dw  48000	; page 4 offset

;****************************************************************************

X320x240:

	db  0e3h        ; 480 scan lines & 25 mhz clock
	dw  05f00h      ; horz total
	dw  04f01h      ; horz displayed
	dw  05002h      ; start horz blanking
	dw  08203h      ; end horz blanking
	dw  05404h      ; start h sync
	dw  08005h      ; end h sync
	dw  00d06h      ; vertical total
	dw  03e07h      ; overflow
	dw  0ea10h      ; v sync start
	dw  08c11h      ; v sync end/prot cr0 cr7
	dw  0df12h      ; vertical displayed
	dw  0e715h      ; v blank start
	dw  00616h      ; v blank end
	dw  04109h      ; cell height (2 scan lines)
	dw  00014h      ; dword mode off
	dw  0e317h      ; turn on byte mode
	dw  19200/4     ; page size (dwords)
	dw  320         ; xmax
	dw  240         ; ymax
	dw  0		; page 1 offset
	dw  19200	; page 2 offset
	dw  38400	; page 3 offset
	dw  0       	; page 4 offset (same as page 1)

;****************************************************************************

X320x400:

	db  063h        ; 400 scan lines & 25 mhz clock
	dw  05f00h      ; horz total
	dw  04f01h      ; horz displayed
	dw  05002h      ; start horz blanking
	dw  08203h      ; end horz blanking
	dw  05404h      ; start h sync
	dw  08005h      ; end h sync
	dw  0bf06h      ; vertical total
	dw  01f07h      ; overflow
	dw  09c10h      ; v sync start
	dw  08e11h      ; v sync end/prot cr0 cr7
	dw  08f12h      ; vertical displayed
	dw  09615h      ; v blank start
	dw  0b916h      ; v blank end
	dw  04009h      ; cell height (1 scan line)
	dw  00014h      ; dword mode off
	dw  0e317h      ; turn on byte mode
	dw  32000/4     ; page size (dwords)
	dw  320         ; xmax
	dw  400         ; ymax
	dw  0		; page 1 offset
	dw  32000	; page 2 offset
	dw  0		; page 3 offset (same as page 1)
	dw  32000	; page 4 offset (same as page 2)

;****************************************************************************

X320x480:

	db  0e3h        ; 480 scan lines & 25 mhz clock
	dw  05f00h      ; horz total
	dw  04f01h      ; horz displayed
	dw  05002h      ; start horz blanking
	dw  08203h      ; end horz blanking
	dw  05404h      ; start h sync
	dw  08005h      ; end h sync
	dw  00d06h      ; vertical total
	dw  03e07h      ; overflow
	dw  0ea10h      ; v sync start
	dw  08c11h      ; v sync end/prot cr0 cr7
	dw  0df12h      ; vertical displayed
	dw  0e715h      ; v blank start
	dw  00616h      ; v blank end
	dw  04009h      ; cell height (1 scan line)
	dw  00014h      ; dword mode off
	dw  0e317h      ; turn on byte mode
	dw  38400/4     ; page size (dwords)
	dw  320         ; xmax
	dw  480         ; ymax
	dw  0		; page 1 offset
	dw  0		; page 2 offset (same as page 1)
	dw  0    	; page 3 offset (same as page 1)
	dw  0       	; page 4 offset (same as page 1)

;****************************************************************************

X360x200:

	db  067h        ; 400 scan lines & 28 mhz clock
	dw  06b00h      ; horz total
	dw  05901h      ; horz displayed
	dw  05a02h      ; start horz blanking
	dw  08e03h      ; end horz blanking
	dw  05e04h      ; start h sync
	dw  08a05h      ; end h sync
	dw  0bf06h      ; vertical total
	dw  01f07h      ; overflow
	dw  09c10h      ; v sync start
	dw  08e11h      ; v sync end/prot cr0 cr7
	dw  08f12h      ; vertical displayed
	dw  09615h      ; v blank start
	dw  0b916h      ; v blank end
	dw  04109h      ; cell height (2 scan lines)
	dw  00014h      ; dword mode off
	dw  0e317h      ; turn on byte mode
	dw  18000/4     ; page size (dwords)
	dw  360         ; xmax
	dw  200         ; ymax
	dw  0		; page 1 offset
	dw  18000	; page 2 offset
	dw  36000	; page 3 offset
	dw  0		; page 4 offset (same as page 1)

;****************************************************************************

X360x240:

	db  0e7h        ; 480 scan lines & 28 mhz clock
	dw  06b00h      ; horz total
	dw  05901h      ; horz displayed
	dw  05a02h      ; start horz blanking
	dw  08e03h      ; end horz blanking
	dw  05e04h      ; start h sync
	dw  08a05h      ; end h sync
	dw  00d06h      ; vertical total
	dw  03e07h      ; overflow
	dw  0ea10h      ; v sync start
	dw  08c11h      ; v sync end/prot cr0 cr7
	dw  0df12h      ; vertical displayed
	dw  0e715h      ; v blank start
	dw  00616h      ; v blank end
	dw  04109h      ; cell height (2 scan lines)
	dw  00014h      ; dword mode off
	dw  0e317h      ; turn on byte mode
	dw  21600/4     ; page size (dwords)
	dw  360         ; xmax
	dw  240         ; ymax
	dw  0		; page 1 offset
	dw  21600	; page 2 offset
	dw  43200	; page 3 offset
	dw  0		; page 4 offset (same as page 1)

;****************************************************************************

X360x400:

	db  067h        ; 400 scan lines & 28 mhz clock
	dw  06b00h      ; horz total
	dw  05901h      ; horz displayed
	dw  05a02h      ; start horz blanking
	dw  08e03h      ; end horz blanking
	dw  05e04h      ; start h sync
	dw  08a05h      ; end h sync
	dw  0bf06h      ; vertical total
	dw  01f07h      ; overflow
	dw  09c10h      ; v sync start
	dw  08e11h      ; v sync end/prot cr0 cr7
	dw  08f12h      ; vertical displayed
	dw  09615h      ; v blank start
	dw  0b916h      ; v blank end
	dw  04009h      ; cell height (1 scan line)
	dw  00014h      ; dword mode off
	dw  0e317h      ; turn on byte mode
	dw  36000/4     ; page size (dwords)
	dw  360         ; xmax
	dw  400         ; ymax
	dw  0		; page 1 offset
	dw  0		; page 2 offset (same as page 1)
	dw  0		; page 3 offset (same as page 1)
	dw  0		; page 4 offset (same as page 1)

;****************************************************************************

X360x480:

	db  0e7h        ; 480 scan lines & 28 mhz clock
	dw  06b00h      ; horz total
	dw  05901h      ; horz displayed
	dw  05a02h      ; start horz blanking
	dw  08e03h      ; end horz blanking
	dw  05e04h      ; start h sync
	dw  08a05h      ; end h sync
	dw  00d06h      ; vertical total
	dw  03e07h      ; overflow
	dw  0ea10h      ; v sync start
	dw  08c11h      ; v sync end/prot cr0 cr7
	dw  0df12h      ; vertical displayed
	dw  0e715h      ; v blank start
	dw  00616h      ; v blank end
	dw  04009h      ; cell height (1 scan line)
	dw  00014h      ; dword mode off
	dw  0e317h      ; turn on byte mode
	dw  43200/4     ; page size (dwords)
	dw  360         ; xmax
	dw  480         ; ymax
	dw  0		; page 1 offset
	dw  0		; page 2 offset (same as page 1)
	dw  0		; page 3 offset (same as page 1)
	dw  0		; page 4 offset (same as page 1)

;****************************************************************************

	attrib_ctrl     equ 03c0h       ; vga attribute controller
	gc_index        equ 03ceh       ; vga graphics controller
	sc_index        equ 03c4h       ; vga sequencer controller
	sc_data         equ 03c5h       ; vga sequencer data port
	crtc_index      equ 03d4h       ; vga crt controller
	crtc_data       equ 03d5h       ; vga crt controller data
	misc_output     equ 03c2h       ; vga misc register
	input_1         equ 03dah       ; input status #1 register

	dac_write_addr  equ 03c8h       ; vga dac write addr register
	dac_read_addr   equ 03c7h       ; vga dac read addr register
	pel_data_reg    equ 03c9h       ; vga dac/pel data register r/w

	pixel_pan_reg   equ 033h        ; attrib index: pixel pan reg
	map_mask        equ 002h        ; sequ index: write map mask reg
	read_map        equ 004h        ; gc index: read map register
	start_disp_hi   equ 00ch        ; crtc index: display start hi
	start_disp_lo   equ 00dh        ; crtc index: display start lo

	map_mask_plane1 equ 00102h      ; map register + plane 1
	map_mask_plane2 equ 01102h      ; map register + plane 1
	all_planes_on   equ 00f02h      ; map register + all bit planes

	chain4_off      equ 00604h      ; chain 4 mode off
	async_reset     equ 00100h      ; (a)synchronous reset
	sequ_restart    equ 00300h      ; sequencer restart

	latches_on      equ 00008h      ; bit mask + data from latches
	latches_off     equ 0ff08h      ; bit mask + data from cpu

	vert_retrace    equ 08h         ; input_1: vertical retrace bit
	plane_bits      equ 03h         ; bits 0-1 of xpos = plane #
	all_planes      equ 0fh         ; all bit planes selected
	char_bits       equ 0fh         ; bits 0-3 of character data

;****************************************************************************

	PUBLIC          rows
	PUBLIC          pages
	PUBLIC          xmax
	PUBLIC          ymax
	PUBLIC          vxmin
	PUBLIC          vymin
	PUBLIC          vxmax
	PUBLIC          vymax

;****************************************************************************

	rows            dw 800 dup(0)   ; addresses to all rows in the screen
	pages		dw 4   dup(0)	; offsets of all video pages
	scr_width       dw ?            ; screen width in pixels
	pagesize        dw ?
	xmax            dw ?
	ymax            dw ?
	vxmin           dw ?
	vymin           dw ?
	vxmax           dw ?
	vymax           dw ?

;****************************************************************************

	PUBLIC  xinitvideo

xinitvideo PROC mode:word

	push    ds
	push    es
	push    si
	push    di

	and	mode,00000111b          ; make sure mode is in range 0->7

	cli
	vwait                           ; wait for start of vertical retrace
	mov	dx,3c8h			; clear palette to remove flicker
	xor     al,al
	out     dx,al
	inc     dx
	mov     cx,768
xcp1:
	out	dx,al
	loop	xcp1
	vwait
	sti
	mov     ax,0013h                ; set normal 320x200 graphics mode
	int     10h
	cli
	mov	dx,3c8h                 ; clear palette again
	xor     al,al
	out     dx,al
	inc     dx
	mov     cx,768
xcp2:
	out	dx,al
	loop	xcp2
	vwait

	out_16  sc_index,chain4_off     ; disable chain 4 mode
	out_16  sc_index,async_reset    ; (a)synchronous reset
	mov     bx,mode                 ; get xmode
	shl     bx,1                    ; multiply it by 2
	mov     si,word ptr xmode[bx]   ; point to Xmode table
	mov     al,byte ptr cs:[si]     ; get crtc data from table
	out_8   misc_output,al          ; set new timing/size
	out_16  sc_index,sequ_restart   ; restart sequencer ...

	out_8   crtc_index,11h          ; select vert retrace end register
	inc     dx                      ; point to data
	in      al,dx                   ; get value, bit 7 = protect
	and     al,7fh                  ; mask out write protect
	out     dx,al                   ; and send it back

	mov     dx,crtc_index           ; vga crtc registers
	add     si,1                    ; advance pointer
	mov	cx,16			; we are going to send out 16 words
xiv1:
	mov     ax,word ptr cs:[si]     ; get crtc data from table
	add     si,2                    ; advance pointer
	out     dx,ax                   ; reprogram vga crtc reg
	loop    xiv1                    ; do it 16 times

	mov     ax,word ptr cs:[si]     ; get pagesize from table
	mov     pagesize,ax             ; update pagesize var
	mov     ax,word ptr cs:[si+2]   ; get xmax from table
	mov     xmax,ax                 ; update xmax var
	mov	vxmax,ax
	mov     ax,word ptr cs:[si+4]   ; get ymax from table
	mov     ymax,ax                 ; update ymax var
	mov	vymax,ax
	mov     ax,xmax                 ; get xmax
	shr     ax, 2                   ; bytes = pixels / 4
	mov     scr_width, ax           ; save width in bytes
	shr     ax,1                    ; offset value = bytes / 2
	mov     ah,13h                  ; crtc offset register index
	xchg    al,ah                   ; switch format for out
	out     dx,ax                   ; set vga crtc offset reg

	mov     cx,800                  ; get # of rows
	mov     bx,offset rows          ; point to rows table
	xor     ax,ax                   ; clear ax
xiv2:
	mov     cs:[bx],ax              ; put row offset in table
	add     ax,scr_width            ; get next row offset
	add     bx,2                    ; advance pointer
	loop    xiv2                    ; loop until i'ts done

	mov 	ax,cs                   ; get code segment
	mov	ds,ax                   ; load ds with it
	mov	es,ax                   ; and es too
	mov	cx,4                    ; cx=counter=4
	add	si,6                    ; point si to xmode specific pages
	mov     di,offset pages         ; point di to pages table
	rep 	movsw                   ; move 4 words from ds:si to es:di

	mov	vxmin,0
	mov	vymin,0

	sti

	pop     di
	pop     si
	pop     es
	pop     ds

	ret

xinitvideo      ENDP

;****************************************************************************

	PUBLIC  xclrvram

xclrvram PROC

	push    es
	push    di

	mov     dx,003c4h       ; update all 4 bit-planes at the same time
	mov     ax,00f02h
	out     dx,ax

	cld                     ; block fill forwards
	mov     ax,0a000h       ; clear vram (all planes)
	mov     es,ax
	xor     di,di
	mov     cx,16384
	xor     eax,eax
	rep     stosd

	pop     di
	pop     es

	ret

xclrvram        ENDP

;****************************************************************************

	PUBLIC  xclrvpage

xclrvpage PROC  vpage:word

	push    es
	push    di

	mov     ax,0a000h
	mov     es,ax                   ; point to vga memory segment
	mov	bx,vpage                ; get page #
	and	bx,00000011b            ; make sure page # is in range 0->3
	shl	bx,1                    ; scale bx
	mov     di,cs:pages[bx]         ; get page offset
	out_16  sc_index,all_planes_on  ; select all planes
	xor     eax,eax                 ; fill page with color 0
	cld                             ; block fill forwards
	mov     cx,pagesize             ; get size of page
	rep     stosd                   ; block fill vga memory

	pop     di
	pop     es

	ret

xclrvpage       ENDP

;****************************************************************************

	PUBLIC  xsetvpage

xsetvpage PROC  vpage:word

	mov	bx,vpage                ; get page #
	and	bx,00000011b            ; make sure page # is in range 0->3
	shl	bx,1                    ; scale bx
	mov     bx,cs:pages[bx]         ; get page offset

	mov     dx,03d4h                ; we change the vga sequencer

	mov     al,00ch                 ; display start high register
	mov     ah,bh                   ; high 8 bits of start addr
	out     dx,ax                   ; set display addr high

	mov     al,00dh                 ; display start low register
	mov     ah,bl                   ; low 8 bits of start addr
	out     dx,ax                   ; set display addr low

	ret

xsetvpage       ENDP

;****************************************************************************

	PUBLIC 	xplot

xplot   PROC 	x:word, y:word, color:byte, vpage:word

	push    es
	push    di

	mov     ax,0a000h
	mov     es,ax                   ; point to vga memory segment

	mov	bx,vpage                ; get page #
	and	bx,00000011b            ; make sure page # is in range 0->3
	shl	bx,1                    ; scale bx
	mov     di,cs:pages[bx]         ; get page offset

	mov	bx,y                    ; get y coord
	shl     bx,1                    ; y=y*2
	add	di,cs:rows[bx]          ; get offset to start of line

	mov     bx,x                    ; get x
	mov     cx,bx                   ; copy to extract plane # from
	shr     bx,2                    ; x offset (bytes) = x/4
	add     di,bx                   ; offset = width*y + x/4

	mov     ax,map_mask_plane1      ; map mask & plane select register
	and     cl,plane_bits           ; get plane bits
	shl     ah,cl                   ; get plane select value
	out_16  sc_index,ax             ; select plane

	mov     al,byte ptr color       ; get pixel color
	mov     byte ptr es:[di],al     ; draw pixel

	pop     di
	pop     es

	ret

xplot ENDP

;****************************************************************************

	PUBLIC 	xget

xget    PROC 	x:word, y:word, vpage:word

	push    es
	push    di

	mov     ax,0a000h
	mov     es,ax                   ; point to vga memory segment

	mov	bx,vpage                ; get page #
	and	bx,00000011b            ; make sure page # is in range 0->3
	shl	bx,1                    ; scale bx
	mov     di,cs:pages[bx]         ; get page offset

	mov	bx,y                    ; get y coord
	shl     bx,1                    ; y=y*2
	add	di,cs:rows[bx]          ; get offset to start of line

	mov     bx,x                    ; get x
	mov     cx,bx                   ; copy to extract plane # from
	shr     bx,2                    ; x offset (bytes) = x/4
	add     di,bx                   ; offset = width*y + x/4

	mov     ax,read_map     	; read map register
	and     cl,plane_bits           ; get plane bits
	out_16  gc_index,ax             ; select plane

	mov     al,byte ptr es:[di]       ; get pixel color

	pop     di
	pop     es

	ret

xget    ENDP

;****************************************************************************

bmpx    dw      ?
bmpy    dw      ?
skew    dw      ?
offi    dw      ?
paste   dw      ?

;****************************************************************************

	PUBLIC  ximageput     	; for now ignores remaining 4 or less pixels

ximageput PROC 	bitmap:dword, x:word, y:word, vpage:word

	push    ds
	push    es
	push    si
	push    di

	mov     ds,offset bitmap+2      ; get bitmap data segment
	mov     si,offset bitmap        ; get bitmap data offset
	mov     ax,0A000h               ;
	mov     es,ax                   ; point to vga memory segment

	mov	bx,vpage                ; get page #
	and	bx,00000011b            ; make sure page # is in range 0->3
	shl	bx,1                    ; scale bx
	mov     di,cs:pages[bx]         ; get page offset

	mov	bx,y                    ; get y coord
	shl     bx,1                    ; y=y*2
	add	di,cs:rows[bx]          ; add offset to start of line
	mov     bx,x                    ; get x
	mov     cx,bx                   ; copy to extract plane # from
	xor     ch,ch                   ; ignore high word
	shr     bx,2                    ; x offset (bytes) = x/4
	add     di,bx                   ; add x offset
	mov     offi,di                 ; keep di in var offi for later use

	xor     bx,bx                   ; clear bx

	lodsw                           ; get bitmap width
	mov     skew,ax                 ; keep it in var skew
	and     skew,00000011b          ; skew now has # of remaining pixels
	shr     ax,2                    ; ax=ax/4
	mov     bmpx,ax                 ; bmpx=bitmap width/4
	lodsw                           ; get bitmap height
	mov     bmpy,ax                 ; keep it in var bmpy
	add     bitmap,4                ; point to bitmap data
	mov	paste,0                 ; clear paste var
xdbmp1:
	inc     ch                      ; ch will be used as a counter (4)
	mov     ax,map_mask_plane1      ; map mask & plane select register
	and     cl,plane_bits           ; get plane bits
	shl     ah,cl                   ; get plane select value
	out_16  sc_index,ax             ; select plane
	push    cx
	call    xdb                     ; draw in it
	pop     cx
	cmp     ch,4                    ; all planes done ?
	je      xdbmp3                  ; if so, end
	cmp     paste,0                 ; are we in a new video ram offset ?
	je      xdbmp2                  ; if not, continue
	add     di,1                    ; if so, inc destination index
xdbmp2:
	add     cl,1                    ; next plane(cl will have plane bits)
	cmp     cl,4                    ; inc plane
	jne     xdbmp1                  ;
	xor     cl,cl
	mov     paste,1
	add     di,1
	jmp     xdbmp1
xdbmp3:
	pop     di
	pop     si
	pop     es
	pop     ds

	ret

xdb     PROC NEAR

	mov     cx,bmpy                 ; loop bmpy times
xdb1:
	push    cx
	mov     cx,bmpx                 ; loop bmpx times
xdb2:
	movsb                           ; plot pixel (and advance 1 pixel)
	add     si,3                    ; advance 3 pixels
	loop    xdb2
	pop     cx
	add     si,skew                 ; step over bitmap skew times
	add     di,scr_width            ; goto next line
	sub     di,bmpx
	loop    xdb1
	mov     di,offi                 ; goto begining vram offset
	inc     bx
	mov     si,offset bitmap        ; point to bitmap data
	add     si,bx                   ; plus bx

	ret

xdb     ENDP

ximageput ENDP

;****************************************************************************

	PUBLIC  ximageadd     	; for now ignores remaining 4 or less pixels

ximageadd PROC 	bitmap:dword, x:word, y:word, vpage:word

	push    ds
	push    es
	push    si
	push    di

	mov     ds,offset bitmap+2      ; get bitmap data segment
	mov     si,offset bitmap        ; get bitmap data offset
	mov     ax,0A000h               ;
	mov     es,ax                   ; point to vga memory segment

	mov	bx,vpage                ; get page #
	and	bx,00000011b            ; make sure page # is in range 0->3
	shl	bx,1                    ; scale bx
	mov     di,cs:pages[bx]         ; get page offset

	mov	bx,y                    ; get y coord
	shl     bx,1                    ; y=y*2
	add	di,cs:rows[bx]          ; add offset to start of line
	mov     bx,x                    ; get x
	mov     cx,bx                   ; copy to extract plane # from
	xor     ch,ch                   ; ignore high word
	shr     bx,2                    ; x offset (bytes) = x/4
	add     di,bx                   ; add x offset
	mov     offi,di                 ; keep di in var offi for later use

	xor     bx,bx                   ; clear bx

	lodsw                           ; get bitmap width
	mov     skew,ax                 ; keep it in var skew
	and     skew,00000011b          ; skew now has # of remaining pixels
	shr     ax,2                    ; ax=ax/4
	mov     bmpx,ax                 ; bmpx=bitmap width/4
	lodsw                           ; get bitmap height
	mov     bmpy,ax                 ; keep it in var bmpy
	add     bitmap,4                ; point to bitmap data
	mov	paste,0                 ; clear paste var
xia1:
	inc     ch                      ; ch will be used as a counter (4)
	mov     ax,map_mask_plane1      ; map mask & plane select register
	and     cl,plane_bits           ; get plane bits
	shl     ah,cl                   ; get plane select value
	out_16  sc_index,ax             ; select plane

	mov     al,read_map     	; read map register
	mov	ah,cl
	out_16  gc_index,ax             ; select plane

	push    cx
	call    xadd                    ; draw in it
	pop     cx
	cmp     ch,4                    ; all planes done ?
	je      xia3                    ; if so, end
	cmp     paste,0                 ; are we in a new video ram offset ?
	je      xia2                    ; if not, continue
	add     di,1                    ; if so, inc destination index
xia2:
	add     cl,1                    ; next plane(cl will have plane bits)
	cmp     cl,4                    ; inc plane
	jne     xia1                    ;
	xor     cl,cl
	mov     paste,1
	add     di,1
	jmp     xia1
xia3:
	pop     di
	pop     si
	pop     es
	pop     ds

	ret

xadd    PROC NEAR

	mov     cx,bmpy                 ; loop bmpy times
xadd1:
	push    cx
	mov     cx,bmpx                 ; loop bmpx times
xadd2:
	mov	al,es:[di]
	add	al,ds:[si]
	mov     es:[di],al              ; plot pixel
	add     si,4                    ; advance 3 pixels
	add	di,1
	loop    xadd2
	pop     cx
	add     si,skew                 ; step over bitmap skew times
	add     di,scr_width            ; goto next line
	sub     di,bmpx
	loop    xadd1
	mov     di,offi                 ; goto begining vram offset
	inc     bx
	mov     si,offset bitmap        ; point to bitmap data
	add     si,bx                   ; plus bx

	ret

xadd     ENDP

ximageadd ENDP

;****************************************************************************

	PUBLIC	xresize

xresize PROC 	x:word, y:word

	mov	ax,x			; get new screen width
	mov     vxmax,ax                ; save new screen width
	shr     ax,2                    ; bytes = pixels / 4
	mov     scr_width, ax           ; save width in bytes

	mov	ax,y                    ; get new screen height
	mov	vymax,ax                ; save new screen height
	mov	cx,800
	mov     bx,offset rows          ; point to rows table
	xor     ax,ax                   ; clear ax
xr1:
	mov     cs:[bx],ax              ; put row offset in table
	add     ax,scr_width            ; get next row offset
	add     bx,2                    ; advance pointer
	loop    xr1                     ; loop until i'ts done

	mov	bx,8 			; bx = 8 pixels wide
	mov	ax,x 			; ax = number of pixels per row
	div     bl              	; al = number of character columms
	xchg	ah,al           	; ah = number of character columms

	mov	al,13h         		; CRTC offset register number
	out_16	crtc_index,ax

	ret

xresize ENDP

;****************************************************************************

	PUBLIC	xpanscr

xpanscr	PROC 	x:word, y:word

	mov	dx,3dah       		; video status port
	in   	al,dx                	; reset flip-flop
	mov  	dx,3c0h             	; attribute controller port
	mov  	al,13h or 20h      	; horiz pel pan reg number OR 0x20
	out  	dx,al                	; write attribute controller address reg
	mov  	al,byte ptr x       	; get pixel x coordinate
	and  	al,3                 	; value for horiz pel pan reg
	shl	al,1
	out  	dx,al                	; write horiz pel pan reg

	mov	bx,y			; get pixel y coordinate
	xor	dx,dx
	mov  	ax,x    	   	; get pixel x coordinate
	div	vxmax
	add	bx,ax
	mov	vymin,bx                ; save new virtual minimum x
	shl	bx,1                    ; scale bx
	mov	bx,cs:rows[bx]          ; get row offset from table
	mov	vxmin,dx                ; save new virtual minimum x
	mov	ax,dx
	shr  	ax,2                 	; divide by 4 (for X-mode)
	add  	bx,ax                	; save it for later use

	mov  	dx,03d4h 	      	; vga crt controller
	mov  	al,0ch			; start address high reg number
	mov  	ah,bh                	; get start address high
	out  	dx,ax                	; write start address high

	mov  	al,0dh              	; start address low reg number
	mov  	ah,bl                	; get start address low
	out  	dx,ax                	; write start address low

	ret

xpanscr	ENDP

;****************************************************************************

	PUBLIC 	xsplit

xsplit	PROC 	n:word

	mov	dx,3d4h
	mov	ax,n
	mov	vymax,ax
	mov	bh,ah
	mov	bl,bh
	and	bx,0201h
	mov	cl,4
	shl	bx,cl
	shl	bh,1
	mov	ah,al
	mov	al,18h
	out	dx,ax
	mov	al,7
	out	dx,ax
	inc	dx
	in	al,dx
	dec	dx
	mov	ah,al
	and	ah,11101111b
	or	ah,bl
	mov	al,7
	out	dx,ax
	mov	al,9
	out	dx,al
	inc	dx
	in	al,dx
	dec	dx
	mov	ah,al
	and	ah,10111111b
	or	ah,bh
	mov	al,9
	out	dx,ax

	ret

xsplit	ENDP

;****************************************************************************

	PUBLIC  xlineputcy

xlineputcy PROC pointer:dword, ya:word, x:word, y:word, vpage:word

	push    ds
	push    es
	push    si
	push    di

	mov	ax,0a000h
	mov	es,ax

	mov	bx,vpage                ; get page #
	and	bx,00000011b            ; make sure page # is in range 0->3
	shl	bx,1                    ; scale bx
	mov     di,cs:pages[bx]         ; get page offset

	mov	bx,y                    ; get y coord
	shl     bx,1                    ; y=y*2
	add	di,cs:rows[bx]          ; get offset to start of line

	mov     bx,x                    ; get x
	mov     cx,bx                   ; copy to extract plane # from
	shr     bx,2                    ; x offset (bytes) = x/4
	add     di,bx                   ; offset = width*y + x/4

	mov     ax,map_mask_plane1      ; map mask & plane select register
	and     cl,plane_bits           ; get plane bits
	shl     ah,cl                   ; get plane select value
	out_16  sc_index,ax             ; select plane

	mov     si,offset pointer
	mov     ds,offset pointer+2

	xor     bx,bx
	xor     dx,dx
	xor	cx,cx
plcy1:
	mov     cl,byte ptr ds:[si]
	add     bx,cx
	mov     al,byte ptr ds:[si+1]
plcy2:
	mov     byte ptr es:[di],al
	add     di,scr_width
	loop    plcy2
	add     si,2
	add     dx,2
	cmp     bx,ya
	jl      plcy1
	mov     ax,dx

	pop     di
	pop     si
	pop     es
	pop     ds

	ret

xlineputcy ENDP

;****************************************************************************

	PUBLIC  xblock

xblock  PROC 	x1:word, y1:word, count:word, y2:word, color:byte, vpage:word

	push    es
	push    di

	mov	ax,0a000h
	mov	es,ax

	mov	bx,vpage                ; get page #
	and	bx,00000011b            ; make sure page # is in range 0->3
	shl	bx,1                    ; scale bx
	mov     di,cs:pages[bx]         ; get page offset

	mov	bx,y1                   ; get y coord
	shl     bx,1                    ; y=y*2
	add	di,cs:rows[bx]          ; get offset to start of line

	mov     bx,x1                   ; get x
	shr     bx,2                    ; x offset (bytes) = x/4
	add     di,bx                   ; offset = width*y + x/4

	mov     dx,003c4h       ; update all 4 bit-planes at the same time
	mov     ax,00f02h
	out     dx,ax

	mov	al,color
	mov	cx,count
xb1:
	push	di
	push	cx
	mov	cx,y2
	sub	cx,y1
xb2:
	mov     byte ptr es:[di],al
	add     di,scr_width
	loop    xb2
	pop	cx
	pop	di
	inc	di
	loop	xb1

	pop     di
	pop     es

	ret

xblock	ENDP

;****************************************************************************

	PUBLIC	xvwait

xvwait 	PROC

	mov   	dx,03DAh
xvw1:
	in    	al,dx
	test  	al,8
	jnz     xvw1	  	; wait Until out of vertical retrace
xvw2:
	in    	al,dx
	test  	al,8
	jz    	xvw2 	  	; wait Until inside vertical retrace

	ret

xvwait 	ENDP

;****************************************************************************

	PUBLIC	xhwait

xhwait 	PROC

	mov   	dx,03DAh
xhw1:
	in    	al,dx
	test  	al,1
	jnz     xhw1	  	; wait Until out of vertical retrace
xhw2:
	in    	al,dx
	test  	al,1
	jz    	xhw1 	  	; wait Until inside vertical retrace

	ret

xhwait 	ENDP

;******************************************************************************

	PUBLIC	xsetborder

xsetborder	PROC color:byte

	push	ax
	push	dx
	cli
	mov	dx,3dah
	in	al,dx
	mov	dx,3c0h
	mov	al,11h+32
	out	dx,al
	mov	al,color
	out	dx,al
	sti
	pop	dx
	pop	ax

	ret

xsetborder 	ENDP

;******************************************************************************

	PUBLIC	xsetborder2

xsetborder2	PROC red:byte, green:byte, blue:byte

	push	ax
	push	dx
	cli
	mov	dx,3c8h
        mov     al,0
        out     dx,al
        mov     dx,3c9h
        mov     al,red
        out     dx,al
        mov     al,green
        out     dx,al
        mov     al,blue
        out     dx,al

	sti
	pop	dx
	pop	ax

	ret

xsetborder2 	ENDP

;******************************************************************************

	PUBLIC	xscreenoff

xscreenoff	PROC mode:byte


	mov	dx,3dah                 ; reset flip-flop
	in	al,dx
	out_8   attrib_ctrl,0           ; disable screen

	out_8	sc_index,1		; vga sequencer, index 1
	inc	dx                      ; vga sequencer data port
	in  	al,dx                   ; get previous settings
	or  	al,00100000b            ; set bit 5 (turn off screen)
	out 	dx,al                   ; restore new settings

	ret

xscreenoff 	ENDP

;******************************************************************************

	PUBLIC	xscreenon

xscreenon	PROC mode:byte

	mov	dx,3dah			; reset flip-flop
	in	al,dx
	out_8   attrib_ctrl,20h         ; enable screen

	out_8	sc_index,1		; vga sequencer, index 1
	inc	dx                      ; vga sequencer data port
	in  	al,dx                   ; get previous settings
	and  	al,11011111b            ; reset bit 5 (turn on screen)
	out 	dx,al                   ; restore new settings

	ret

xscreenon     	ENDP

;******************************************************************************

	PUBLIC  xsetpalette

xsetpalette     PROC FAR        pointer: dword

	push    ds
	push    si

	cli
	mov     cx,256
	mov     ds,word ptr pointer+2
	mov     si,word ptr pointer
	mov     dx,03c8h
	mov     al,0
	out     dx,al
	inc     dx
sp1:
	lodsb
	out     dx,al
	lodsb
	out     dx,al
	lodsb
	out     dx,al
	loop    sp1
	sti

	pop     si
	pop     ds

	ret

xsetpalette ENDP

;******************************************************************************

	PUBLIC  xgetpalette

xgetpalette     PROC FAR        firstreg:word, numregs:word, palette:dword

	push	es
	push    di

	mov     cx,numregs
	jcxz    xgp2
	les     di,palette
	cld

	mov     dx,03C7h
	mov     ax,firstreg
	out     dx,al
	mov     dx,03C9h

xgp1:
	in      al,dx
	shl     al,2
	stosb           		; red
	in      al,dx
	shl     al,2
	stosb           		; green
	in      al,dx
	shl     al,2
	stosb           		; blue
	loop    xgp1

xgp2:
	pop     di
	pop	es

	ret

xgetpalette ENDP

;******************************************************************************

	PUBLIC  xsetrgb

xsetrgb         PROC FAR    color:byte, red:byte, green:byte, blue:byte

	cli
	mov     dx,03c8h
	mov     al,color
	out     dx,al
	inc     dx

	mov	al,red
	out     dx,al
	mov	al,green
	out     dx,al
	mov	al,blue
	out     dx,al
	sti

	ret

xsetrgb ENDP

;******************************************************************************

	PUBLIC  xtextmode

xtextmode       PROC FAR    mode:byte

	mov 	ax,03h        ; set text mode
	int 	10h

	cmp	mode,25       ; 50 lines mode ?
	je	no50

	mov 	ax,1112h      ; set font height to 8 pixels
	xor 	bx,bx
	int 	10h
no50:
	ret

xtextmode	ENDP

;******************************************************************************

apage 	dw ?

;******************************************************************************

	PUBLIC	movewin

movewin PROC 	buffer1:dword, buffer2:dword, vpage:word

	push	ds
	push	si
	push    es
	push    di

	mov	ax,0a000h
	mov	es,ax

	mov	bx,vpage                ; get page #
	and	bx,00000011b            ; make sure page # is in range 0->3
	shl	bx,1                    ; scale bx
	mov	ax,pages[bx]
	add	ax,9
	mov 	apage,ax

	lds	si,buffer1
	lfs	bx,buffer2

	mov     dx,003c4h
	mov     ax,0102h
mw1:
	push	ax
	mov	di,apage         	; get page offset
	out     dx,ax
	mov	cx,200
mw0:
	push	cx
	mov	cx,15
mw2:
	mov     eax,dword ptr ds:[si]
	and	eax,dword ptr fs:[bx]
	mov     dword ptr es:[di],eax
	add	si,4
	add	bx,4
	add	di,4
	loop	mw2
	pop	cx
	add	di,20
	add	si,20
	add	bx,20
	loop	mw0
	mov	ax,ds
	add	ax,405
	mov	ds,ax
	mov	ax,fs
	add	ax,405
	mov	fs,ax
	pop	ax
	shl	ah,1
	cmp	ah,8
	jbe	mw1

	pop     di
	pop     es
	pop     si
	pop     ds

	ret

movewin ENDP


END
