; 320x200x256
; 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
	dw	?
	dw	deInitGraphics
	dw	?
        dw	putImage
        dw	?
        dw	putImageInv
        dw	?
        dw	getImage
        dw	?
        dw	putLine
        dw	?
        dw	getLine
        dw	?
        dw	imageSize
        dw	?
	dw      putPixel
	dw	?
	dw	getPixel
	dw	?
	dw	line
        dw	?
        dw	horizLine
	dw	?
	dw	drawRect
	dw	?
        dw	filledRect
	dw	?
	dw	setPaletteReg
	dw	?
        dw	getPaletteReg
        dw	?
	dw	setBlockPalette
	dw	?
        dw	getBlockPalette
        dw	?
        dw	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	319
	dw	199
	dw	255
        dw      8			; mode aspect ratio 8:5 (equal to
	dw	5			; width/depth in lowest terms).

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

LABEL	output	DATAPTR
	DW	0
	DW	0A000h
preInitMode	DB	3

inited	DB	0
colourPalette	DB	768	DUP(?)
tempPalette	DB	768	DUP(?)
lineOffs	DW	200	DUP(?)

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

	PUBLIC C	initGraphics
PROC	C	initGraphics
  cmp	[inited],0
  je	@@L1				; skip if already done

  mov	ax,0013h
  int	10h
  SETPALETTE
  mov	ax,1
  retf

	@@L1:
  mov	[inited],1
  mov	ax,1200h			; enable default palette loading
  mov	bl,31h
  int	10h
  mov	ax,0013h			; turn on 320x200x256
  int	10h

  mov	si,OFFSET lineOffs
  mov	cx,200
  xor	dx,dx

	@@LLoop:
  push	dx
  mov	ax,320
  mul	dx
  mov	[cs:si],ax
  add	si,2
  pop	dx
  inc	dx
  loop	@@LLoop

  mov	ax,1
  retf
ENDP

	PUBLIC	C	deInitGraphics
PROC	C	deInitGraphics
  GETPALETTE
  xor	ah,ah				; switch modes
  mov	al,[byte ptr preInitMode]
  int	10h
  retf
ENDP

; void far putImage(int, int, void far *);
	PUBLIC	C	putImage
PROC	C	putImage
	ARG	x:WORD,y:WORD,image:DATAPTR
        LOCAL	@@dAdd:WORD,@@sAdd:WORD,@@xSize:WORD
  push	ds si di
  mov	[@@dAdd],320
  mov	[@@sAdd],0
  lds	si,[image]
  les	di,[output]
  cld

  lodsw					; xsize
  or	ax,ax
  jz	@@Exit
  mov	cx,ax
  mov	[@@xSize],cx
  lodsw					; ysize
  or	ax,ax
  jz	@@Exit
  mov	dx,ax

  mov	ax,[x]
  cmp	ax,319				; check x<=319
  jg	@@Exit
  or	ax,ax				; check x>=0
  js	@@xLessThanMin

	@@checkxMax:
  mov	ax,[x]
  add	ax,cx			; right edge of image
  cmp	ax,319
  jg	@@truncateRightImage	; truncate if necessary

  	@@checkyMin:
  mov	ax,[y]
  cmp	ax,199				; check y<=199
  jg	@@Exit
  or	ax,ax				; check y>=0
  js	@@yLessThanMin

  	@@checkyMax:
  mov	ax,[y]
  add	ax,dx			; bottom of image
  cmp	ax,200
  jg	@@truncateBottomImage	; truncate if necessary

  jmp	short	@@drawImage	; jump to draw logic

	@@xLessThanMin:			; clip x to 0
  mov	[x],0
  neg	ax			; ax=x from before--# of pixels<0
  add	si,ax
  sub	cx,ax
  cmp	cx,0
  jle	@@Exit			; quit if offscreen
  mov	[@@sAdd],ax
  jmp	short	@@checkxMax

	@@Exit:				; quit putImage()
  pop	di si ds
  leave
  retf

  	@@truncateRightImage:		; truncate the excess of right edge
  sub	ax,320			; ax=right edge from before
  sub	cx,ax
  mov	[@@sAdd],ax
  jmp	short	@@checkyMin

  	@@yLessThanMin:			; clip y to 0
  mov	[y],0
  neg	ax			; ax=y from before
  sub	dx,ax
  cmp	dx,0
  jle	@@Exit			; quit if offscreen
  push	dx cx
  mov	cx,[@@xSize]
  mul	cx
  pop	cx dx
  add	si,ax
  jmp	short	@@checkyMax

	@@truncateBottomImage:		; clip y to 199
  sub	ax,200			; ax=bottom from before
  sub	dx,ax			; fall through to @@drawImage

  	@@drawImage:			; label for @@yGreaterThanMax jmp
; calculate the pixel address
  push	dx
  mov	dx,[x]
  mov	ax,[y]
  xchg	ah,al			; ax=256*y
  add	dx,ax			; dx=256*y+x
  shr	ax,2			; ax=64*y
  add	dx,ax			; dx=320*y+x
  mov	di,dx
  pop	dx

  mov	ax,[@@dAdd]
  sub	ax,cx				; update dAdd

  shr	cx,1
  jc	@@LoddNum
  mov	bx,cx				; MOVSW to be used
  jmp	short	@@LEven2
  	@@LEven1:
  add	si,[@@sAdd]
  add	di,ax			; dAdd
  mov	cx,bx
  	@@LEven2:
  rep	movsw
  dec	dx
  jnz	@@LEven1
  jmp	short	@@Exit

	@@LoddNum:			; MOVSW + MOVSB to be used
  mov	bx,cx				; preserve xsize
  jmp	short	@@LOdd2			; only first iteration
  	@@LOdd1:			; copy the bitmap
  add	si,[@@sAdd]
  add	di,ax			; dAdd
  mov	cx,bx
  	@@LOdd2:
  rep	movsw
  movsb
  dec	dx
  jnz	@@LOdd1
  jmp	short	@@Exit
ENDP

; void far putImageInv(int, int, void far *);
	PUBLIC	C	putImageInv
PROC	C	putImageInv
	ARG	x:WORD,y:WORD,image:DATAPTR
        LOCAL	@@dAdd:WORD,@@sAdd:WORD,@@xSize:WORD
  push	ds si di
  mov	[@@dAdd],320
  mov	[@@sAdd],0
  lds	si,[image]
  les	di,[output]
  cld

  lodsw					; xsize
  or	ax,ax
  jz	@@Exit
  mov	cx,ax
  mov	[@@xSize],cx
  lodsw					; ysize
  or	ax,ax
  jz	@@Exit
  mov	dx,ax

  mov	ax,[x]
  cmp	ax,319				; check x<=319
  jg	@@Exit
  or	ax,ax				; check x>=0
  js	@@xLessThanMin

	@@checkxMax:
  mov	ax,[x]
  add	ax,cx			; right edge of image
  cmp	ax,319
  jg	@@truncateRightImage	; truncate if necessary

  	@@checkyMin:
  mov	ax,[y]
  cmp	ax,199				; check y<=199
  jg	@@Exit
  or	ax,ax				; check y>=0
  js	@@yLessThanMin

  	@@checkyMax:
  mov	ax,[y]
  add	ax,dx			; bottom of image
  cmp	ax,200
  jg	@@truncateBottomImage	; truncate if necessary

  jmp	short	@@drawImage	; jump to draw logic

	@@xLessThanMin:			; clip x to 0
  mov	[x],0
  neg	ax			; ax=x from before--# of pixels<0
  add	si,ax
  sub	cx,ax
  cmp	cx,0
  jle	@@Exit			; quit if offscreen
  mov	[@@sAdd],ax
  jmp	short	@@checkxMax

	@@Exit:				; quit putImage()
  pop	di si ds
  leave
  retf

  	@@truncateRightImage:		; truncate the excess of right edge
  sub	ax,320			; ax=right edge from before
  sub	cx,ax
  mov	[@@sAdd],ax
  jmp	short	@@checkyMin

  	@@yLessThanMin:			; clip y to 0
  mov	[y],0
  neg	ax			; ax=y from before
  sub	dx,ax
  cmp	dx,0
  jle	@@Exit			; quit if offscreen
  push	dx cx
  mov	cx,[@@xSize]
  mul	cx
  pop	cx dx
  add	si,ax
  jmp	short	@@checkyMax

	@@truncateBottomImage:		; clip y to 199
  sub	ax,200			; ax=bottom from before
  sub	dx,ax			; fall through to @@drawImage

  	@@drawImage:			; label for @@yGreaterThanMax jmp
; calculate the pixel address
  push	dx
  mov	dx,[x]
  mov	ax,[y]
  xchg	ah,al			; ax=256*y
  add	dx,ax			; dx=256*y+x
  shr	ax,2			; ax=64*y
  add	dx,ax			; dx=320*y+x
  mov	di,dx
  pop	dx

  sub	[@@dAdd],cx			; update dAdd
  mov	bx,cx
  jmp	short	@@xLoop
  	@@yLoop:
  add	di,[@@dAdd]
  add	si,[@@sAdd]
  mov	cx,bx

  	@@xLoop:
  lodsb
  or	al,al
  jz	@@Invisible
  stosb
  loop	@@xLoop
  dec	dx
  jnz	@@yLoop
  jmp	short	@@Exit

  	@@Invisible:
  inc	di
  loop	@@xLoop

  dec	dx
  jnz	@@yLoop
  jmp	short	@@Exit
ENDP

; void far getImage(int, int, int, int, void far *);
	PUBLIC	C	getImage
PROC	C	getImage
	ARG	x1:WORD,y1:WORD,x2:WORD,y2:WORD,image:DATAPTR
  push	ds si di
  cmp	[x1],0				; Screen boundries
  jge	@@L1
  mov	[x1],0
	@@L1:
  cmp	[y1],0
  jge	@@L2
  mov	[y1],0
	@@L2:
  cmp	[x2],0
  jge	@@L3
  mov	[x2],0
	@@L3:
  cmp	[y2],0
  jge	@@L4
  mov	[y2],0
	@@L4:
  cmp	[x1],319
  jle	@@L5
  mov	[x1],319
	@@L5:
  cmp	[y1],199
  jle	@@L6
  mov	[y1],199
	@@L6:
  cmp	[x2],319
  jle	@@L7
  mov	[x2],319
	@@L7:
  cmp	[y2],199
  jle	@@L8
  mov	[y2],199
	@@L8:
  mov	ax,[x2]				; Make sure x2>=x1
  cmp	ax,[x1]
  jge	@@L9
  mov	dx,[x1]
  mov	[x2],dx
  mov	[x1],ax
	@@L9:
  mov	ax,[y2]				; Make sure y2>=y1
  cmp	ax,[y1]
  jge	@@L10
  mov	dx,[y1]
  mov	[y2],dx
  mov	[y1],ax
	@@L10:
  cld
  lds	si,[output]			; set up DS:SI
  mov	bx,[y1]
  shl	bx,1
  add	si,[cs:lineOffs+bx]
  add	si,[x1]

  les	di,[image]
  mov	ax,[x2]
  sub	ax,[x1]
  or	ax,ax
  js	@@LExit				; just in case 0-width image
  inc	ax
  push	ax
  stosw
  mov	ax,[y2]
  sub	ax,[y1]
  or	ax,ax
  js	@@LExit				; just in case 0-depth image
  inc	ax
  stosw

  mov	ax,[x2]				; calculate sAdd
  sub	ax,[x1]
  mov	dx,319
  sub	dx,ax
  push	dx				; save sAdd for later

  mov	ax,[y2]				; y loop counter
  sub	ax,[y1]
  mov	dx,ax
  inc	dx

  pop	ax
  pop	cx				; pop pushed width (x loop counter)

  shr	cx,1
  jc	@@LOdd
  mov	bx,cx				; MOVSW to be used
  jmp	short	@@LoopEvenEntry
  	@@LoopEven:
  add	si,ax
  mov	cx,bx
	@@LoopEvenEntry:
  rep	movsw
  dec	dx
  jnz	@@LoopEven
  jmp	short	@@LExit

	@@LOdd:				; MOVSW + MOVSB to be used
  mov	bx,cx
  jmp	short	@@LoopOddEntry
	@@LoopOdd:
  add	si,ax			; sAdd
  mov	cx,bx
	@@LoopOddEntry:
  rep	movsw
  movsb
  dec	dx
  jnz	@@LoopOdd

	@@LExit:
  pop	di si ds
  leave
  retf
ENDP

; void far putLine(unsigned lineNum, unsigned xOff, unsigned lineLen, \
;    void far *buf);
	PUBLIC	C	putLine
PROC	C	putLine
	ARG	lineNum:WORD, xOff:WORD, lineLen:WORD, buf:DATAPTR
  push	ds si di
  cld
  les	di,[output]
  lds	si,[buf]

  mov	bx,[lineNum]
  shl	bx,1
  add	di,[cs:lineOffs+bx]
  add	di,[xOff]
  mov	cx,[lineLen]

  shr	cx,1
  rep	movsw
  jc	@@L1
  pop	di si ds
  leave
  retf

	@@L1:
  movsb
  pop	di si ds
  leave
  retf
ENDP

; void far getLine(unsigned lineNum, unsigned xOff, unsigned lineLen, \
;    void far *buf);
	PUBLIC	C	getLine
PROC	C	getLine
	ARG	lineNum:WORD, xOff:WORD, lineLen:WORD, buf:DATAPTR
  cld
  push	ds si di
  les	di,[buf]
  lds	si,[output]

  mov	bx,[lineNum]
  shl	bx,1
  add	si,[cs:lineOffs+bx]
  add	si,[xOff]
  mov	cx,[lineLen]

  shr	cx,1
  rep	movsw
  jc	@@L1
  pop	di si ds
  leave
  retf

	@@L1:
  movsb
  pop	di si ds
  leave
  retf
ENDP

; void far imageSize(int, int, int, int);
	PUBLIC	C	imageSize
PROC	C	imageSize
	ARG	x1:WORD,y1:WORD,x2:WORD,y2:WORD
  cmp	[x1],0				; Screen boundries
  jge	@@L1
  mov	[x1],0
	@@L1:
  cmp	[y1],0
  jge	@@L2
  mov	[y1],0
	@@L2:
  cmp	[x2],0
  jge	@@L3
  mov	[x2],0
	@@L3:
  cmp	[y2],0
  jge	@@L4
  mov	[y2],0
	@@L4:
  cmp	[x1],319
  jle	@@L5
  mov	[x1],319
	@@L5:
  cmp	[y1],199
  jle	@@L6
  mov	[y1],199
	@@L6:
  cmp	[x2],319
  jle	@@L7
  mov	[x2],319
	@@L7:
  cmp	[y2],199
  jle	@@L8
  mov	[y2],199
	@@L8:
  mov	ax,[x2]				; Make sure x2>=x1
  cmp	ax,[x1]
  jge	@@L9
  mov	dx,[x1]
  mov	[x2],dx
  mov	[x1],ax
  	@@L9:
  mov	ax,[y2]				; Make sure y2>=y1
  cmp	ax,[y1]
  jge	@@L10
  mov	dx,[y1]
  mov	[y2],dx
  mov	[y1],ax
  	@@L10:
  mov	dx,[x2]
  sub	dx,[x1]
  inc	dx
  mov	ax,[y2]
  sub	ax,[y1]
  inc	ax
  mul	dx				; Product in AX for return
  add	ax,4				; Add 4 for dimensions, return in AX
  xor	dx,dx				; returns unsigned long
  leave
  retf
ENDP

; void putPixel(x,y,colour)
; int x,y;
; unsigned char colour
	PUBLIC	C	putPixel
PROC	C	putPixel
	ARG	x:WORD,y:WORD,colour:BYTE
  mov   ax,0A000h
  mov	es,ax
  mov	bx,[y]
  shl	bx,1
  mov	bx,[cs:lineOffs+bx]
  add	bx,[x]
  mov	al,[colour]
  mov	[es:bx],al
  leave
  retf
ENDP

; unsigned char getPixel(x,y)
; int x,y;
	PUBLIC	C	getPixel
PROC	C	getPixel
	ARG	x:WORD,y:WORD
  mov	ax,0A000h
  mov	es,ax
  mov	bx,[y]
  shl	bx,1
  mov	bx,[cs:lineOffs+bx]
  add	bx,[x]
  mov	al,[byte ptr es:bx]
  xor	ah,ah
  leave
  retf
ENDP

;------ The following "line" routine was taken from Richard Wilton's book
;------ "Programmer's Guide to PC and PS/2 Video Systems".

SCREENSEG	EQU	0A000h

MACRO	PIXELADDR
  xchg	ah,al			; ax=256*y
  add	bx,ax			; bx=256*y+x
  shr	ax,2
  add	bx,ax			; bx=320*y+x (+ offset in buffer if needed.)
  mov	ax,SCREENSEG
  mov	es,ax			; es:bx=address of pixel
ENDM

	MASM

; void line(x1,y1,x2,y2,colour)
; int x1,y1,x2,y2;
; unsigned char colour;
	PUBLIC	C	line
ARGx1		EQU	word ptr [bp+6]
ARGy1		EQU	word ptr [bp+8]
ARGx2		EQU	word ptr [bp+10]
ARGy2		EQU	word ptr [bp+12]
ARGcolour	EQU	byte ptr [bp+14]
VARincr1	EQU	word ptr [bp-2]
VARincr2	EQU	word ptr [bp-4]
VARroutine	EQU	word ptr [bp-6]
line	PROC	FAR
	push	bp
        mov	bp,sp
        sub	sp,6
        push	si
        push	di

; check for vertical lines
	mov	si,320				; initial y-increment
        mov	cx,ARGx2
        sub	cx,ARGx1			; cx=x2-x1
        jz	vertLine

; force x1 < x2
	jns	L01				; jump if x1<x2
        neg	cx				; cx=x1-x2
        mov	bx,ARGx2			; exchange x1 and x2
        xchg	bx,ARGx1
        mov	ARGx2,bx
        mov	bx,ARGy2			; exchange y1 and y2
        xchg	bx,ARGy1
        mov	ARGy2,bx

; calculate dy=abs(y2-y1)
L01:	mov	bx,ARGy2
        sub	bx,ARGy1			; bx=y2-y1
        jz	@@horizLine
        jns	L03				; jump if slope is positive
        neg	bx				; bx=y1-y2
        neg	si				; negate y-increment

; select appropriate routine for slope of line
L03:	push	si
	mov	VARroutine,offset loSlopeLine
        cmp	bx,cx
        jle	L04				; jump if dy<=dx (slope <=1)
        mov     VARroutine,offset hiSlopeLine
        xchg	bx,cx				; exchange dy and dx

; calculate initial decision variable and increments
L04:	shl	bx,1				; bx=2*dy
	mov	VARincr1,bx			; incr1=2*dy
        sub	bx,cx
        mov	si,bx				; si=d=2*dy-dx
        sub	bx,cx
        mov	VARincr2,bx			; incr2=2*(dy-dx)

; calculate first pixel address
        push	cx
        mov	ax,ARGy1
        mov	bx,ARGx1
        PIXELADDR
        mov	di,bx
        pop	cx
        inc	cx				; cx=# of pixels to draw
        pop	bx				; bx=y-increment
        jmp	VARroutine			; jump to appropriate routine

; routine for vertical lines
vertLine:	mov	ax,ARGy1
		mov	bx,ARGy2
                mov	cx,bx
                sub	cx,ax			; cx=dy
                jge	L31			; jump if dy>=0
                neg	cx			; force dy>=0
                mov	ax,bx			; ax=y2
L31:	inc	cx				; cx=# of pixels to draw
        mov	bx,ARGx1			; bx=x
        PIXELADDR				; es:bx
        mov	di,bx				; es:di
        dec	si				; si=bytes/line-1
        mov	al,ARGcolour

L32:	stosb
	add	di,si				; next line
        loop	L32
        jmp	short	Lexit

; routine for horizontal lines (slope=0)
@@horizLine:	push	cx
		mov	ax,ARGy1
                mov	bx,ARGx1
                PIXELADDR			; es:bx
                mov	di,bx			; es:di
                pop	cx
                inc	cx			; cx=# of pixels to draw
                mov	al,ARGcolour
                rep	stosb
                jmp	short	Lexit

; routine for dy<=dx (slope<=1)			; es:di -> video buffer
						; bx=y-increment
                                                ; cx =# of pixels to draw
                                                ; si=decision variable
loSlopeLine:    mov	al,ARGcolour

L11:	stosb					; put pixel, increment x
	or	si,si				; test sign of d
        jns	L12				; jump id d>=0
        add	si,VARincr1			; d=d+incr1
        loop	L11
        jmp	short	Lexit

L12:    add	si,VARincr2			; d=d+incr2
	add	di,bx				; increment y
        loop	L11
        jmp	short	Lexit

; routine for dy>dx (slope >1)			; es:di -> video buffer
						; bx=y-increment
                                                ; cx=# of pixels to draw
                                                ; si=decision variable
hiSlopeLine:    mov	al,ARGcolour

L21:	stosb					; put pixex, increment x
	add	di,bx				; increment y

L22:	or	si,si				; test sign of d
	jns	L23				; jump if d>=0
        add	si,VARincr1			; d=d+incr1
        dec	di				; dec x (inc by stosb)
        loop	L21
        jmp	short	Lexit

L23:    add	si,VARincr2			; d=d+incr2
	loop	L21

Lexit:	pop	di
	pop	si
	mov	sp,bp
        pop	bp
        ret
line	ENDP

	IDEAL

	PUBLIC	C	horizLine
PROC	C	horizLine
	ARG	y:WORD, x1:WORD, x2:WORD, colour:BYTE
  push	di

  les	di,[output]
  mov	bx,[y]
  mov	ax,[x1]
  mov	cx,[x2]
  sub	cx,ax
  inc	cx

  shl	bx,1
  add	di,[cs:lineOffs+bx]
  add	di,ax

  mov	al,[colour]
  mov	ah,al
  shr	cx,1
  rep	stosw
  jnc	@@LExit
  stosb

  	@@LExit:
  pop	di
  leave
  retf
ENDP

	PUBLIC	C	drawRect
PROC	C	drawRect
	ARG	x1:WORD, y1:WORD, x2:WORD, y2:WORD, colour:BYTE
  push	si di
  les	di,[output]

  mov	ax,[y1]		; y coords
  mov	bx,[y2]
  cmp	ax,bx
  jbe	@@L1
  xchg	ax,bx

	@@L1:
  push	bx
  sub	bx,ax				; ySize

  mov	dx,320				; update DI
  mul	dx
  add	di,ax

  mov	dx,[x1]		; x coords
  mov	cx,[x2]
  cmp	dx,cx
  jbe	@@L2
  xchg	cx,dx

	@@L2:
  add	di,dx				; update DI

  sub	cx,dx				; xSize
  inc	cx
  push	cx cx

  mov	al,[colour]			; colour
  mov	ah,al

  shr	cx,1
  rep	stosw
  jnc	@@VLine
  stosb

	@@VLine:
  pop	cx				; SI = dAdd2
  mov	si,320
  sub	si,cx
  sub	cx,2				; CX = dAdd1
  xchg	cx,bx				; CX = ySize, BX = dAdd1
  jcxz	@@HLine2

	@@LLoop:
  add	di,si
  stosb
  add	di,bx
  stosb
  loop	@@LLoop

	@@HLine2:
  pop	cx bx
  push  ax dx
  mov	ax,320
  mul	bx
  pop	dx
  xor	di,di
  add	di,ax
  pop	ax
  add	di,dx
  shr	cx,1
  rep	stosw
  jnc	@@HLine3
  stosb

	@@HLine3:
  pop	di si
  leave
  retf
ENDP

	PUBLIC	C	filledRect
PROC	C	filledRect
	ARG	x1:WORD, y1:WORD, x2:WORD, y2:WORD, colour:BYTE
  push	di si
  les	di,[output]

  mov	ax,[y1]		; y coords
  mov	bx,[y2]
  cmp	ax,bx
  jbe	@@L1
  xchg	ax,bx

	@@L1:
  sub	bx,ax				; ySize in BX
  inc	bx
  mov	si,320				; update DI
  mul	si
  add	di,ax

  mov	dx,[x1]		; x coords
  mov	cx,[x2]
  cmp	dx,cx
  jbe	@@L2
  xchg	cx,dx

	@@L2:
  add	di,dx

  sub	cx,dx				; xSize in CX
  inc	cx

  sub	si,cx				; xAdd in SI

  mov	al,[colour]			; colour
  mov	ah,al

  cld
  shr	cx,1
  mov	dx,cx
  jc	@@LOddEntry
  jmp	short	@@LEvenEntry

	@@LEvenNum:			; MOVSW to be used
  add	di,si
  mov	cx,dx
	@@LEvenEntry:
  rep	stosw
  dec	bx
  jnz	@@LEvenNum

  pop   si di
  leave
  retf

	@@LOddNum:
  add   di,si
  mov	cx,dx
	@@LOddEntry:
  rep	stosw
  stosb
  dec	bx
  jnz	@@LOddNum

  pop	si di
  leave
  retf
ENDP

; void far setPaletteReg(unsigned palNum, red, green, blue);
	PUBLIC	C	setPaletteReg
PROC	C	setPaletteReg
	ARG	palNum:WORD,red:BYTE,green:BYTE,blue:BYTE
  mov	bx,[palNum]

  mov	dh,[red]			; red
  shr	dh,2
  jnc	@@L1
  cmp	dh,63
  je	@@L1
  inc	dh

	@@L1:
  mov	ch,[green]			; green
  shr	ch,2
  jnc	@@L2
  cmp	ch,63
  je	@@L2
  inc	ch

  	@@L2:
  mov	cl,[blue]
  shr	cl,2
  jnc	@@L3
  cmp	cl,63
  je	@@L3
  inc	cl

  	@@L3:
  mov	ax,1010h
  int	10h
  leave
  retf
ENDP

; void far getPaletteReg(unsigned palNum, void far *red, *green, *blue);
	PUBLIC	C	getPaletteReg
PROC	C	getPaletteReg
	ARG	palNum:WORD,red:DATAPTR,green:DATAPTR,blue:DATAPTR
  push	ds si
  mov	ax,1015h
  mov	bx,[palNum]
  int	10h
  lds	si,[red]
  shl	dh,2
  mov	[ds:si],dh
  lds	si,[green]
  shl	ch,2
  mov	[ds:si],ch
  lds	si,[blue]
  shl	cl,2
  mov	[ds:si],cl
  pop	si ds
  leave
  retf
ENDP

; void far setBlockPalette(unsigned, unsigned, void far *)
	PUBLIC	C	setBlockPalette
PROC	C	setBlockPalette
	ARG	firstReg:WORD,numRegs:WORD,paletteData:DATAPTR
  push	ds si di
  lds	si,[paletteData]
  push	cs
  pop	es
  mov	di,OFFSET tempPalette
  mov	cx,[numRegs]
  jcxz	@@LExit
  push	cx
  cld

	@@LLoop:
  lodsb			; red
  shr	al,2
  jnc	@@L1
  cmp	al,63
  je	@@L1
  inc	al
  	@@L1:
  stosb
  lodsb			; green
  shr	al,2
  jnc	@@L2
  cmp	al,63
  je	@@L2
  inc	al
  	@@L2:
  stosb
  lodsb			; blue
  shr	al,2
  jnc	@@L3
  cmp	al,63
  je	@@L3
  inc	al
  	@@L3:
  stosb
  loop	@@LLoop

  mov	ax,1012h
  mov	bx,[firstReg]			; first colour reg
  pop	cx				; # of colour regs
  mov	dx,OFFSET tempPalette		; ES = CS
  int	10h

	@@LExit:
  pop	di si ds
  leave
  retf
ENDP

; void far getBlockPalette(unsigned, unsigned, void far *)
	PUBLIC	C	getBlockPalette
PROC	C	getBlockPalette
	ARG	firstReg:WORD,numRegs:WORD,paletteData:DATAPTR
  push	ds si di
  mov	ax,1017h
  mov	bx,[firstReg]
  mov	cx,[numRegs]
  jcxz	@@LExit
  push	cs
  pop	es
  mov	dx,OFFSET tempPalette
  int	10h

  les	di,[paletteData]
  mov	ax,cs
  mov	ds,ax
  mov	si,OFFSET tempPalette
  mov	cx,[numRegs]
  cld

	@@L1:
  lodsb
  shl	al,2
  stosb		; red
  lodsb
  shl	al,2
  stosb		; green
  lodsb
  shl	al,2
  stosb		; blue
  loop	@@L1

	@@LExit:
  pop	di si ds
  leave
  retf
ENDP

	PUBLIC	C	clearGraphics
PROC	C	clearGraphics
	ARG	colour:BYTE
  push	di
  les	di,[output]
  mov	cx,32000
  mov	al,[colour]
  mov	ah,al
  rep	stosw
  pop	di
  leave
  retf
ENDP

ENDS
END