; 320x400x256 (requires register-compatible VGA+)
; loadable driver for The Graphics Engine
; Copyright (c) 1994 by Matthew Hildebrand

; Turbo Assembler syntax

IDEAL
P286
MODEL LARGE

	CODESEG
	ORG	0

	db	'GRAP'
	dw	initGraphics		; initGraphics
	dw	?
	dw	deInitGraphics		; deInitGraphics
	dw	?
        dw	0			; putImage
        dw	?
        dw	0			; putImageInv
        dw	?
        dw	0			; getImage
        dw	?
        dw	putLine			; putLine
        dw	?
        dw	getLine			; getLine
        dw	?
        dw	0			; imageSize
        dw	?
	dw      putPixel		; putPixel
	dw	?
	dw	getPixel		; getPixel
	dw	?
	dw	0			; line
        dw	?
        dw	horizLine		; horizLine
	dw	?
	dw	0			; drawRect
	dw	?
        dw	0			; filledRect
	dw	?
	dw	setPaletteReg		; setPaletteReg
	dw	?
        dw	getPaletteReg		; getPaletteReg
	dw	?
	dw	setBlockPalette		; setBlockPalette
	dw	?
        dw	getBlockPalette		; getBlockPalette
        dw	?
        dw	clearGraphics		; clearGraphics
        dw	?
	dw	319
	dw	399
	dw	255

LABEL	output	DATAPTR
	dw	0
	dw	0A000h

	db	'The Graphics Engine -- Copyright (c) 1993 by Matthew Hildebrand'

inited		db	0
colourPalette	db	768	DUP(?)
lineOffs	dw	400	DUP(?)

; Plane masks for horizLine
leftEdgeMask	db	1111b, 1110b, 1100b, 1000b
rightEdgeMask	db	0001b, 0011b, 0111b, 1111b


SCREEN_SEG	EQU	0A000h
SC_INDEX	EQU	3C4h		; Sequence Controller Index register
GC_INDEX	EQU	3CEh		; Graphics Controller Index register
CRTC_INDEX	EQU	3D4h		; CRT Controller Index register
MAP_MASK	EQU	2		; Map Mask register index in SC
MEMORY_MODE	EQU	4		; Memory Mode register index in SC
MAX_SCAN_LINE	EQU	9		; Maximum Scan Line reg index in CRTC
START_ADDR_HIGH	EQU	0Ch		; Start Address High reg index in CRTC
UNDERLINE	EQU	14h		; Underline Location reg index in CRTC
MODE_CONTROL	EQU	17h		; Mode Control register index in CRTC
READ_MAP	EQU	4		; Read Map register index in GC
GRAPHICS_MODE	EQU	5		; Graphics Mode register index in GC
MISCELLANEOUS	EQU	6		; Miscellaneous register index in GC
SCREEN_WIDTH	EQU	320		; Width of screen in pixels
SCREEN_HEIGHT	EQU	400		; Height of screen in pixels
WORD_OUTS_OK	EQU	1		; Set to 0 for VGAs that can't handle
					; word OUTs to indexed VGA registers

MACRO	GETPALETTE
  mov	ax,1017h
  xor	bx,bx
  mov	cx,256
  mov	dx,cs
  mov	es,dx
  mov	dx,OFFSET colourPalette
  int	10h
ENDM

MACRO	SETPALETTE
  mov	ax,1012h
  xor	bx,bx
  mov	cx,256
  mov	dx,cs
  mov	es,dx
  mov	dx,OFFSET colourPalette
  int	10h
ENDM

MACRO	OUT_WORD
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

MACRO	CONSTANT_TO_INDEXED_REG ADDRESS, INDEX, VALUE
  mov	dx,ADDRESS
  mov	ax,(VALUE shl 8) + INDEX
  OUT_WORD
ENDM


	PUBLIC	C	initGraphics
PROC	C	initGraphics
  mov	ax,0013h			; set mode 13h
  int	10h

  ; change CPU addressing to linear
  mov	dx,SC_INDEX
  mov	al,MEMORY_MODE
  out	dx,al
  inc	dx
  in	al,dx
  and	al,NOT 08h			; turn off chain 4
  or 	al,04h				; turn off odd/even
  out	dx,al
  mov	dx,GC_INDEX
  mov	al,GRAPHICS_MODE
  out	dx,al
  inc	dx
  in	al,dx
  and	al,NOT 10h			; turn off odd/even
  out	dx,al
  dec	dx
  mov	al,MISCELLANEOUS
  out	dx,al
  inc	dx
  in	al,dx
  and	al,NOT 02h			; turn off chain
  out	dx,al

  ; clear all 256k of video RAM
  CONSTANT_TO_INDEXED_REG SC_INDEX, MAP_MASK, 0Fh	; enable writes to all planes
  mov	ax,SCREEN_SEG
  mov	es,ax
  xor	di,di
  mov	ax,di
  mov	cx,8000h			; # of words in 64k
  cld
  rep	stosw				; clear all of display RAM

  ; Tweak the mode to 320x400 by not scanning each line twice
  mov	dx,CRTC_INDEX
  mov	al,MAX_SCAN_LINE
  out	dx,al
  inc	dx
  in	al,dx
  and	al,NOT 1Fh			; set max scan line to 0
  out	dx,al
  dec	dx

  ; Change CRTC scanning from DWORD to BYTE mode, allowing the CRTC to scan
  ; more than 64k of video data.
  mov	al,UNDERLINE
  out	dx,al
  inc	dx
  in	al,dx
  and	al,NOT 40h			; turn off doubleword
  out	dx,al
  dec	dx
  mov	al,MODE_CONTROL
  out	dx,al
  inc	dx
  in	al,dx
  or	al,40h				; turn on byte mode bit
  out	dx,al

  mov	al,[inited]			; restore palette if necessary
  or	al,al
  jz	@@notInited
  SETPALETTE
  mov	ax,1
  retf

  	@@notInited:
  mov	[inited],1
  push	si
  mov	si,OFFSET lineOffs
  mov	cx,400
  xor	bx,bx

	@@LLoop:
  mov	ax,SCREEN_WIDTH/4
  mul	bx
  mov	[cs:si],ax
  add	si,2
  inc	bx
  loop	@@LLoop
  pop	si
  mov	ax,1
  retf
ENDP

	PUBLIC	C	deInitGraphics
PROC	C	deInitGraphics
  GETPALETTE
  mov	ax,0003h
  int	10h
  retf
ENDP

	PUBLIC	C	putLine
PROC	C	putLine
	ARG	lineNum:WORD, xOff:WORD, lineLen:WORD, buf:DATAPTR
        LOCAL	x2:WORD
  push	ds si di

  mov	bx,[lineNum]			; start address in ES:DI
  shl	bx,1
  mov	di,[lineOffs+bx]
  mov	dx,[xOff]
  shr	dx,2
  add	di,dx
  mov	ax,SCREEN_SEG
  mov	es,ax
  lds	si,[buf]			; source in DS:SI
  cld

  mov	bx,[xOff]
  add	bx,[lineLen]			; calculate x2
  dec	bx
  mov	[x2],bx

  mov	cx,[lineLen]			; line is short
  cmp	cx,4
JUMPS
  jle	@@ShortLine
NOJUMPS

  mov	cx,[xOff]			; calculate starting plane
  mov	bx,cx
  mov	ax,0102h
  and	cl,3
  shl	ah,cl
  mov	dx,SC_INDEX
  OUT_WORD
  push	si
  mov	bx,[x2]
  mov	cx,[xOff]
  	@@Loop1:
  cmp	cx,bx
  jg	@@Loop1Done
  lodsb
  stosb
  add	cx,4
  add	si,3
  jmp	short	@@Loop1

	@@Loop1Done:
  pop	si
  inc	si
  push	si
  mov	bx,[xOff]
  inc	bx
  mov	cx,bx
  mov	ax,0102h
  and	cl,3
  shl	ah,cl
  mov	dx,SC_INDEX
  OUT_WORD
  mov	cx,bx
  mov	bx,[lineNum]			; start address in ES:DI
  shl	bx,1
  mov	di,[lineOffs+bx]
  shr	cx,2
  add	di,cx
  mov	bx,[x2]
  mov	cx,[xOff]
  inc	cx
  	@@Loop2:
  cmp	cx,bx
  jg	@@Loop2Done
  lodsb
  stosb
  add	cx,4
  add	si,3
  jmp	short	@@Loop2

  	@@Loop2Done:
  pop	si
  inc	si
  push	si
  mov	bx,[xOff]
  add	bx,2
  mov	cx,bx
  mov	ax,0102h
  and	cl,3
  shl	ah,cl
  mov	dx,SC_INDEX
  OUT_WORD
  mov	cx,bx
  mov	bx,[lineNum]
  shl	bx,1
  mov	di,[lineOffs+bx]
  shr	cx,2
  add	di,cx
  mov	bx,[x2]
  mov	cx,[xOff]
  add	cx,2
  	@@Loop3:
  cmp	cx,bx
  jg	@@Loop3Done
  lodsb
  stosb
  add	cx,4
  add	si,3
  jmp	short	@@Loop3

  	@@Loop3Done:
  pop	si
  inc	si
  mov	bx,[xOff]
  add	bx,3
  mov	cx,bx
  mov	ax,0102h
  and	cl,3
  shl	ah,cl
  mov	dx,SC_INDEX
  OUT_WORD
  mov	cx,bx
  mov	bx,[lineNum]
  shl	bx,1
  mov	di,[lineOffs+bx]
  shr	cx,2
  add	di,cx
  mov	bx,[x2]
  mov	cx,[xOff]
  add	cx,3
	@@Loop4:
  cmp	cx,bx
  jg	@@Exit
  lodsb
  stosb
  add	cx,4
  add	si,3
  jmp	short	@@Loop4

  	@@ShortLine:
  mov	cx,[xOff]
  mov	bx,cx
  and	cl,3
  mov	ax,0102h
  shl	ah,cl
  mov	dx,SC_INDEX
  OUT_WORD
  mov	cx,bx
  shr	bx,2
  mov	di,bx
  mov	bx,[lineNum]
  shl	bx,1
  add	di,[lineOffs+bx]
  movsb
  cmp	cx,[x2]
  jge	@@Exit
  inc	[xOff]
  jmp	short	@@ShortLine

  	@@Exit:
  pop   di si ds
  leave
  retf
ENDP

	PUBLIC	C	getLine
PROC	C	getLine
	ARG	lineNum:WORD, xOff:WORD, lineLen:WORD, buf:DATAPTR
        LOCAL	x2:WORD
  push	ds si di

  mov	bx,[lineNum]			; start address in ES:DI
  shl	bx,1
  mov	si,[lineOffs+bx]
  mov	dx,[xOff]
  shr	dx,2
  add	si,dx
  mov	ax,SCREEN_SEG
  mov	ds,ax
  les	di,[buf]			; buffer addr in DS:SI
  cld

  mov	bx,[xOff]
  add	bx,[lineLen]			; calculate x2
  dec	bx
  mov	[x2],bx

  mov	cx,[lineLen]			; line is short
  cmp	cx,4
JUMPS
  jle	@@ShortLine
NOJUMPS

  mov	ax,[xOff]			; calculate starting plane
  mov	bx,ax
  and	al,3
  mov	ah,al
  mov	al,READ_MAP
  mov	dx,GC_INDEX
  OUT_WORD
  push	di
  mov	bx,[x2]
  mov	cx,[xOff]
  	@@Loop1:
  cmp	cx,bx
  jg	@@Loop1Done
  lodsb
  stosb
  add	cx,4
  add	di,3
  jmp	short	@@Loop1

	@@Loop1Done:
  pop	di
  inc	di
  push	di
  mov	bx,[xOff]
  inc	bx
  mov	ax,bx
  and	al,3
  mov	ah,al
  mov	al,READ_MAP
  mov	dx,GC_INDEX
  OUT_WORD
  mov	cx,bx
  mov	bx,[lineNum]			; start address in ES:DI
  shl	bx,1
  mov	si,[lineOffs+bx]
  shr	cx,2
  add	si,cx
  mov	bx,[x2]
  mov	cx,[xOff]
  inc	cx
  	@@Loop2:
  cmp	cx,bx
  jg	@@Loop2Done
  lodsb
  stosb
  add	cx,4
  add	di,3
  jmp	short	@@Loop2

  	@@Loop2Done:
  pop	di
  inc	di
  push	di
  mov	bx,[xOff]
  add	bx,2
  mov	ax,bx
  and	al,3
  mov	ah,al
  mov	al,READ_MAP
  mov	dx,GC_INDEX
  OUT_WORD
  mov	cx,bx
  mov	bx,[lineNum]
  shl	bx,1
  mov	si,[lineOffs+bx]
  shr	cx,2
  add	si,cx
  mov	bx,[x2]
  mov	cx,[xOff]
  add	cx,2
  	@@Loop3:
  cmp	cx,bx
  jg	@@Loop3Done
  lodsb
  stosb
  add	cx,4
  add	di,3
  jmp	short	@@Loop3

  	@@Loop3Done:
  pop	di
  inc	di
  mov	bx,[xOff]
  add	bx,3
  mov	ax,bx
  and	al,3
  mov	ah,al
  mov	al,READ_MAP
  mov	dx,GC_INDEX
  OUT_WORD
  mov	cx,bx
  mov	bx,[lineNum]
  shl	bx,1
  mov	si,[lineOffs+bx]
  shr	cx,2
  add	si,cx
  mov	bx,[x2]
  mov	cx,[xOff]
  add	cx,3
	@@Loop4:
  cmp	cx,bx
  jg	@@Exit
  lodsb
  stosb
  add	cx,4
  add	di,3
  jmp	short	@@Loop4

  	@@ShortLine:
  mov	ax,[xOff]
  mov	bx,ax
  and	al,3
  mov	ah,al
  mov	al,READ_MAP
  mov	dx,GC_INDEX
  OUT_WORD
  mov	cx,bx
  shr	bx,2
  mov	si,bx
  mov	bx,[lineNum]
  shl	bx,1
  add	si,[lineOffs+bx]
  movsb
  cmp	cx,[x2]
  jge	@@Exit
  inc	[xOff]
  jmp	short	@@ShortLine

  	@@Exit:
  pop   di si ds
  leave
  retf
ENDP



	PUBLIC	C	putPixel
PROC	C	putPixel
	ARG	x:WORD, y:WORD, colour:BYTE
  push	di

  mov	ax,SCREEN_SEG
  mov	es,ax
  mov	bx,[y]				; point to start of desired row
  shl	bx,1
  mov	bx,[lineOffs+bx]
  mov	dx,[x]
  mov	cx,dx				; store x coord
  shr	dx,2
  add	bx,dx
  mov	di,bx				; ES:DI points to pixel
  and	cl,3				; get the plane # of the pixel
  mov	ax,102h
  shl	ah,cl				; set the bit corresponding to plane
  mov	dx,SC_INDEX
  OUT_WORD
  mov	bl,[colour]
  mov	[es:di],bl

  pop	di
  leave
  retf
ENDP

	PUBLIC	C	getPixel
PROC	C	getPixel
	ARG	x:WORD, y:WORD
  push	di

  mov	ax,SCREEN_SEG
  mov	es,ax
  mov	bx,[y]				; point to start of desired row
  shl	bx,1
  mov	bx,[lineOffs+bx]
  mov	dx,[x]
  mov	cx,dx				; store x coord
  shr	dx,2
  add	bx,dx
  mov	di,bx				; ES:DI points to pixel
  and	cl,3				; get the plane # of the pixel
  mov	al,READ_MAP
  mov	ah,cl
  mov	dx,GC_INDEX			; set the bit corresponding to plane
  OUT_WORD
  xor	ax,ax
  mov	al,[es:di]

  pop	di
  leave
  retf
ENDP

	PUBLIC	C	horizLine
PROC	C	horizLine
	ARG	y:WORD, x1:WORD, x2:WORD, colour:BYTE
        LOCAL	adj1:WORD, adj2:WORD
  push	si di
  cld

  mov	ax,[x1]				; set up adj1
  and	ax,3
  jz	@@adj1A
  mov	bx,4
  sub	bx,ax
  mov	ax,[x1]
  add	ax,bx
  mov	[adj1],ax
  jmp	short	@@adj1B
  	@@adj1A:
  mov	ax,[x1]
  mov	[adj1],ax
  	@@adj1B:

  mov	ax,[x2]				; set up adj2
  mov	[adj2],ax
  and	ax,3
  sub	[adj2],ax

  mov	ax,[x1]				; ensure x1 <= x2
  cmp	ax,[x2]
  jbe	@@setup
  mov	bx,[x2]
  mov	[x1],bx
  mov	[x2],ax

  	@@setup:
  mov	ax,SCREEN_SEG			; set ES:DI to line start address
  mov	es,ax
  mov	bx,[y]
  shl	bx,1
  mov	di,[lineOffs+bx]
  mov	bx,[x1]				; current x counter
  shr	bx,2
  add	di,bx

  mov	cx,[adj2]			; CX = # of 4-pixel sets starting
  shr	cx,2                            ; on plane 0
  mov	bx,[adj1]
  shr	bx,2
  sub	cx,bx

  cmp	cx,1
  jl	@@shortLine

  ; Draw the left edge
  mov	ax,[x1]				; get plane number of pixel #1
  and	ax,3
  jz	@@planeZeroStart		; starts at plane zero
  mov	si,ax				; set for pixels
  mov	ah,[leftEdgeMask+si]
  mov	al,MAP_MASK
  mov	dx,SC_INDEX
  OUT_WORD
  mov	al,[colour]
  stosb

  ; Draw the interior stretch
  	@@planeZeroStart:
  mov	ax,0F02h			; enable writes to all planes
  mov	dx,SC_INDEX
  OUT_WORD
  mov	al,[colour]
  mov	ah,al
  shr	cx,1
  jc	@@Odd
  rep	stosw				; even number of pixels
  jmp	short	@@rightEdge
  	@@Odd:
  rep	stosw				; odd number of pixels
  stosb

  ; Draw the right edge
  	@@rightEdge:
  mov	ax,[x2]				; get plane number of last pixel
  and	ax,3
  mov	si,ax				; set for pixels
  mov	ah,[rightEdgeMask+si]
  mov	al,MAP_MASK
  mov	dx,SC_INDEX
  OUT_WORD
  mov	al,[colour]
  stosb

  	@@Exit:
  pop	di si				; clean up and go home
  leave
  retf

	@@shortLine:			; for very small lines
  mov	al,[colour]
  mov	cx,[x1]
  mov	dx,[y]
  	@@Loop:
  push	ax cx dx			; slow loop until done
  call	putPixel C, cx, dx, ax
  pop	dx cx ax
  inc	cx
  cmp	cx,[x2]
  jle	@@Loop

  pop 	di si
  leave
  retf
ENDP

	PUBLIC	C	setPaletteReg
PROC	C	setPaletteReg
	ARG	palNum:WORD,red:BYTE,green:BYTE,blue:BYTE
  mov	bx,[palNum]

  mov	dx,3C8h				; set for the right palette register
  mov	ax,[palNum]
  out	dx,al
  inc	dx

  mov	al,[red]			; red
  shr	al,2
  jnc	@@L1
  cmp	al,63
  je	@@L1
  inc	al
	@@L1:
  out	dx,al

  mov	al,[green]			; green
  shr	al,2
  jnc	@@L2
  cmp	al,63
  je	@@L2
  inc	al
	@@L2:
  out	dx,al

  mov	al,[blue]			; blue
  shr	al,2
  jnc	@@L3
  cmp	al,63
  je	@@L3
  inc	al
	@@L3:
  out	dx,al

  leave
  retf
ENDP

	PUBLIC	C	getPaletteReg
PROC	C	getPaletteReg
	ARG	palNum:WORD,red:DATAPTR,green:DATAPTR,blue:DATAPTR
  push	ds si

  mov	dx,3C7h				; set for right palette register
  mov	ax,[palNum]
  out	dx,al
  mov	dx,3C9h

  in	al,dx				; red
  lds	si,[red]
  shl	al,2
  mov	[ds:si],al
  in	al,dx				; green
  lds	si,[green]
  shl	al,2
  mov	[ds:si],al
  in	al,dx				; blue
  lds	si,[blue]
  shl	al,2
  mov	[ds:si],al

  pop	si ds
  leave
  retf
ENDP

	PUBLIC	C	setBlockPalette
PROC	C	setBlockPalette
	ARG	firstReg:WORD,numRegs:WORD,paletteData:DATAPTR
  push	ds si
  lds	si,[paletteData]
  mov	cx,[numRegs]
  jcxz	@@LExit
  cld

  mov	dx,3C8h				; set for right first pal reg
  mov	ax,[firstReg]
  out	dx,al
  inc	dx

	@@LLoop:
  lodsb			; red
  shr	al,2
  jnc	@@L1
  cmp	al,63
  je	@@L1
  inc	al
  	@@L1:
  out	dx,al

  lodsb			; green
  shr	al,2
  jnc	@@L2
  cmp	al,63
  je	@@L2
  inc	al
  	@@L2:
  out	dx,al

  lodsb			; blue
  shr	al,2
  jnc	@@L3
  cmp	al,63
  je	@@L3
  inc	al
  	@@L3:
  out	dx,al

  loop	@@LLoop

	@@LExit:
  pop	si ds
  leave
  retf
ENDP

	PUBLIC	C	getBlockPalette
PROC	C	getBlockPalette
	ARG	firstReg:WORD,numRegs:WORD,paletteData:DATAPTR
  push	di
  mov	cx,[numRegs]
  jcxz	@@LExit
  les	di,[paletteData]
  cld

  mov	dx,3C7h				; set for right first pal reg
  mov	ax,[firstReg]
  out	dx,al
  mov	dx,3C9h

	@@L1:
  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	@@L1

	@@LExit:
  pop	di
  leave
  retf
ENDP

	PUBLIC	C	clearGraphics
PROC	C	clearGraphics
	ARG	colour:BYTE
  mov   dx,SC_INDEX
  mov   ax,0F02h
  out   dx,ax   			; enable writes to all four planes

  mov	ax,SCREEN_SEG
  mov	es,ax
  xor	di,di
  cld
  mov	cx,16000
  mov	al,[colour]
  mov	ah,al
  rep	stosw

  leave
  retf
ENDP

	ENDS
END