; 360x480x256 (requires register-compatible VGA+)
; loadable driver for The Graphics Engine
; Copyright (c) 1993 by Matthew Hildebrand

; Turbo Assembler syntax

IDEAL
P286
MODEL LARGE

	CODESEG
	ORG	0

	db	'TGE1'
	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	0			; ellipse
	dw	?
        dw	0			; filledEllipse
        dw	?
        dw	0			; circle
        dw	?
        dw	0			; filledCircle
        dw	?
        dd      20	DUP(?)		; 20 blank function slots for future
	dw	359
	dw	479
	dw	255
        dw      2			; mode aspect ratio 2:3 (equal to
	dw	3			; width/depth in lowest terms).

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

LABEL	output	DATAPTR
	dw	0
	dw	0A000h

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

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


SC_INDEX	EQU	3C4h    ; Sequence Controller Index
GC_INDEX	EQU	3CEh	; Graphics Controller Index register
CRTC_INDEX	EQU	3D4h    ; CRT Controller Index
MISC_OUTPUT	EQU	3C2h    ; Miscellaneous Output register
SCREEN_SEG	EQU	0A000h  ; segment of display memory in mode X
MAP_MASK	EQU     02h     ; index in SC of Map Mask register
READ_MAP	EQU	4	; Read Map register index in GC
SCREEN_WIDTH	EQU	360      ; width of screen in bytes from one scan line
				; to the next
INPUT_STATUS_1  EQU     03DAh   ; Input Status 1 register
START_ADDR_HIGH EQU	0Ch     ; start address high byte
START_ADDR_LOW  EQU	0Dh     ; start address low byte
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


	PUBLIC	C	initGraphics
PROC	C	initGraphics
  push	si di


  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,0F02h			; set write plane mask to all bit planes
  out	dx,ax
  xor	di,di
  mov	ax,SCREEN_SEG
  mov	es,ax
  mov	cx,21600
  xor	ax,ax
  cld
  rep	stosw				; clear the whole of the screen

  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

  mov	ax,06b00h			; horz total
  out	dx,ax
  mov	ax,05901h			; horz displayed
  out	dx,ax
  mov	ax,05a02h			; start horz blanking
  out	dx,ax
  mov	ax,08e03h			; end horz blanking
  out	dx,ax
  mov	ax,05e04h			; start h sync
  out	dx,ax
  mov	ax,08a05h			; end h sync
  out	dx,ax
  mov	ax,00d06h			; vertical total
  out	dx,ax
  mov	ax,03e07h			; overflow
  out	dx,ax
  mov	ax,04009h			; cell height
  out	dx,ax
  mov	ax,0ea10h			; v sync start
  out	dx,ax
  mov	ax,0ac11h			; v sync end and protect cr0-cr7
  out	dx,ax
  mov	ax,0df12h			; vertical displayed
  out	dx,ax
  mov	ax,02d13h			; offset
  out	dx,ax
  mov	ax,00014h			; turn off dword mode
  out	dx,ax
  mov	ax,0e715h			; v blank start
  out	dx,ax
  mov	ax,00616h			; v blank end
  out	dx,ax
  mov	ax,0e317h			; turn on byte mode
  out	dx,ax


  cmp	[inited],0			; restore palette if necessary
  je	@@notInited
  SETPALETTE
  pop	di si
  mov	ax,1
  retf

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

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

  pop   di si
  mov	ax,1
  retf
ENDP

	PUBLIC	C	deInitGraphics
PROC	C	deInitGraphics
  GETPALETTE
  mov	ax,0003
  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,21600
  mov	al,[colour]
  mov	ah,al

  rep	stosw
  leave
  retf
ENDP

	ENDS
END