; *** Listing 10.1 ***
;
; C tiny/small/medium model-callable assembler
; subroutines to:
;	* Set 360x480 256-color VGA mode
;	* Draw a dot in 360x480 256-color VGA mode
;	* Read the color of a dot in 360x480 256-color VGA mode
;
; Assembled with TASM 4.0
; Checked by Jim Mischel 11/21/94
;
; The 360x480 256-color mode set code and parameters were provided
; by John Bridges, who has placed them into the public domain.
;
VGA_SEGMENT	equ	0a000h	;display memory segment
SC_INDEX		equ	3c4h	;Sequence Controller Index register
GC_INDEX		equ	3ceh	;Graphics Controller Index register
MAP_MASK		equ	2	;Map Mask register index in SC
READ_MAP		equ	4	;Read Map register index in GC
SCREEN_WIDTH	equ	360	;# of pixels across screen
WORD_OUTS_OK	equ	1	;set to 0 to assemble for
				; computers that can't handle
				; word outs to indexed VGA registers
;
_DATA	segment	public byte 'DATA'
;
; 360x480 256-color mode CRT Controller register settings.
; (Courtesy of John Bridges.)
;
vptbl   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      04009h  ; cell height
        dw      0ea10h  ; v sync start
        dw      0ac11h  ; v sync end and protect cr0-cr7
        dw      0df12h  ; vertical displayed
        dw      02d13h  ; offset
        dw      00014h  ; turn off dword mode
        dw      0e715h  ; v blank start
        dw      00616h  ; v blank end
        dw      0e317h  ; turn on byte mode
vpend   label   word
_DATA	ends
;
; Macro to output a word value to a port.
;
OUT_WORD	macro
if WORD_OUTS_OK
	out	dx,ax
else
	out	dx,al
	inc	dx
	xchg	ah,al
	out	dx,al
	dec	dx
	xchg	ah,al
endif
	endm
;
_TEXT	segment byte public 'CODE'
	assume	cs:_TEXT, ds:_DATA
;
; Sets up 360x480 256-color mode.
; (Courtesy of John Bridges.)
;
; Call as: void Set360By480Mode()
;
; Returns: nothing
;
	public _Set360x480Mode
_Set360x480Mode	proc	near
	push	si		;preserve C register vars
	push	di
        mov     ax,12h          ; start with mode 12h
        int     10h             ; let the bios clear the video memory
 
        mov     ax,13h          ; start with standard mode 13h
        int     10h             ; let the bios set the mode
 
        mov     dx,3c4h         ; alter sequencer registers
        mov     ax,0604h        ; disable chain 4
        out     dx,ax
 
        mov     ax,0100h        ; synchronous reset
        out     dx,ax           ; asserted
        mov     dx,3c2h         ; misc output
        mov     al,0e7h         ; use 28 mHz dot clock
        out     dx,al           ; select it
        mov     dx,3c4h         ; sequencer again
        mov     ax,0300h        ; restart sequencer
        out     dx,ax           ; running again
 
        mov     dx,3d4h         ; alter crtc registers
 
        mov     al,11h          ; cr11
        out     dx,al           ; current value
        inc     dx              ; point to data
        in      al,dx           ; get cr11 value
        and     al,7fh          ; remove cr0 -> cr7
        out     dx,al           ;    write protect
        dec     dx              ; point to index
        cld
        mov     si,offset vptbl
        mov     cx,((offset vpend)-(offset vptbl)) shr 1
@b:     lodsw
        out     dx,ax
        loop    @b
	pop	di		;restore C register vars
	pop	si
	ret
_Set360x480Mode	endp
;
; Draws a pixel in the specified color at the specified
; location in 360x480 256-color mode.
;
; Call as: void Draw360x480Dot(int X, int Y, int Color)
;
; Returns: nothing
;
DParms	struc
	dw	?	;pushed BP
	dw	?	;return address
DrawX	dw	?	;X coordinate at which to draw
DrawY	dw	?	;Y coordinate at which to draw
Color	dw	?	;color in which to draw (in the
			; range 0-255; upper byte ignored)
DParms	ends
;
	public _Draw360x480Dot
_Draw360x480Dot	proc	near
	push	bp	;preserve caller's BP
	mov	bp,sp	;point to stack frame
	push	si	;preserve C register vars
	push	di
	mov	ax,VGA_SEGMENT
	mov	es,ax	;point to display memory
	mov	ax,SCREEN_WIDTH/4
			;there are 4 pixels at each address, so
			; each 360-pixel row is 90 bytes wide
			; in each plane
	mul	[bp+DrawY] ;point to start of desired row
	mov	di,[bp+DrawX] ;get the X coordinate
	shr	di,1	;there are 4 pixels at each address
	shr	di,1	; so divide the X coordinate by 4
	add	di,ax	;point to the pixel's address
	mov	cl,byte ptr [bp+DrawX] ;get the X coordinate again
	and	cl,3	;get the plane # of the pixel
	mov	ah,1
	shl	ah,cl	;set the bit corresponding to the plane
			; the pixel is in
	mov	al,MAP_MASK
	mov	dx,SC_INDEX
	OUT_WORD		;set to write to the proper plane for
			; the pixel
	mov	al,byte ptr [bp+Color]	;get the color
	stosb		;draw the pixel
	pop	di	;restore C register vars
	pop	si
	pop	bp	;restore caller's BP
	ret
_Draw360x480Dot	endp
;
; Reads the color of the pixel at the specified
; location in 360x480 256-color mode.
;
; Call as: int Read360x480Dot(int X, int Y)
;
; Returns: pixel color
;
RParms	struc
	dw	?	;pushed BP
	dw	?	;return address
ReadX	dw	?	;X coordinate from which to read
ReadY	dw	?	;Y coordinate from which to read
RParms	ends
;
	public _Read360x480Dot
_Read360x480Dot	proc	near
	push	bp	;preserve caller's BP
	mov	bp,sp	;point to stack frame
	push	si	;preserve C register vars
	push	di
	mov	ax,VGA_SEGMENT
	mov	es,ax	;point to display memory
	mov	ax,SCREEN_WIDTH/4
			;there are 4 pixels at each address, so
			; each 360-pixel row is 90 bytes wide
			; in each plane
	mul	[bp+DrawY] ;point to start of desired row
	mov	si,[bp+DrawX] ;get the X coordinate
	shr	si,1	;there are 4 pixels at each address
	shr	si,1	; so divide the X coordinate by 4
	add	si,ax	;point to the pixel's address
	mov	ah,byte ptr [bp+DrawX]
			;get the X coordinate again
	and	ah,3	;get the plane # of the pixel
	mov	al,READ_MAP
	mov	dx,GC_INDEX
	OUT_WORD		;set to read from the proper plane for
			; the pixel
	lods	byte ptr es:[si] ;read the pixel
	sub	ah,ah	;make the return value a word for C
	pop	di	;restore C register vars
	pop	si
	pop	bp	;restore caller's BP
	ret
_Read360x480Dot	endp
_TEXT	ends
	end
