; lores.asm	3/12/84
; interface routines to use 160x100 mode of color graphics
title	LORES 160x100 Graphics Primitives.
	page	62,100

	public	lores

;	public	first_init,cls,plotdot,getdot
;	public	drawline,box,circle
;	public	random,randomize
;	public	letter,loprint,slowletter
;	public	sin,cos,lopaint

;
;	GENERAL FORM OF ALL ROUTINES FROM BASIC:
;
;	CALL LORES%(ROUTINE%,PARMS%(0))
;
;	Where ROUTINE% is what is to be done,
;	  and PARMS%(0) is an integer array of 6 elements with specs for action.
;
;	Unless the routine returns a value, all elements of PARMS%(x)
;	  are returned unchanged.
;

TRUE	equ	-1
FALSE	equ	0

COMPILER	EQU	FALSE	; set to TRUE for compiler .obj file

REGISTER	equ	3d4h
CRT		equ	3d5h
MODE		equ	3d8h
COLORPORT	equ	3d9h
STATUS		equ	3dah

ROM_TABLE_ADDRESS	equ	0fa6eh	; character patterns in ROM
COLOR_CARD_SEGMENT	equ	0b800h	; where the action is

MULT		equ	261	; for random number generator
MODULUS 	equ	32749

abs0	segment at 0
	org	1dh*4
parm_ptr	label	dword	; for video horizontal sync.
				; LORES respects DOS MODE co80,r
abs0	ends


; Equates for drawline
;
;  stack has address of start of parms.
;	V(0)	= X1	= start col. (0-159)
;	V(1)	= Y1	= start row  (0-99)
;	V(2)	= X2	= end column
;	V(3)	= Y2	= end row
;	V(4)	= color = ( 0-15 )
;	V(5)	= length= 0 for draw whole line, else sub-set
;

ARG1	equ	word ptr [BP+4] 	; 4 for near call, 6 for far

X1	equ	word ptr [si]
Y1	equ	word ptr [si+2]
X2	equ	word ptr [si+4]
Y2	equ	word ptr [si+6]
COLOR	equ	byte ptr [si+8]
LEN	equ	word ptr [si+10]

; these are values that will be inserted in the code for DRAWLINE
	INC_X	equ	41H
	DEC_X	equ	49H
	INC_Y	equ	42H
	DEC_Y	equ	4AH

; these are addresses where new code is overlayed for line
ADJ_LONG_AXIS	equ	byte ptr cs:[di]
ADJ_MASTER	equ	word ptr cs:[di+3]
TEST_MASTER	equ	word ptr cs:[di+7]
ALT_ADJ_MASTER	equ	word ptr cs:[di+13]
ADJ_SHRT_AXIS	equ	byte ptr cs:[di+15]


;cgroup  group	 cseg
cseg	segment para public 'code'
	assume	CS:cseg

lores	proc	far		; entry for all lobio routines
	push	bp
	mov	bp,sp
	mov	ax,COLOR_CARD_SEGMENT
	mov	es,ax		; all routines assume es: for video work
	mov	si,[bp+8]	; routine number
	mov	ax,[si]
	sub	ah,ah
	shl	ax,1
	mov	si,[bp+6]	; array(0)
	mov	bx,ax
	cmp	bx,OUT_OF_RANGE
	jae	loret
	jmp	word ptr cs:[bx+offset jump_table]

loret:
	push	ds
	pop	es
	pop	bp
	ret	4

jump_table	label	word
	dw	offset first	; 0 - array values unused, dummy address needed
	dw	offset cls	; 1 - v(0) = color to clear screen
	dw	offset plot	; 2 - x, y, color
	dw	offset get	; 3 - x, y, color returned in v(2)
	dw	offset line	; 4 - x1,y1,x2,y2,color,length(0 for whole line)
	dw	offset box	; 5 - x1,y1,x2,y2,color,length(set to 0 by lores)
	dw	offset circle_setup ; 6 - x,y,radius,aspect num,aspect den,color
	dw	offset letter1	; 7 - x(0-11),y(0-19),ASC(letter$),color
	dw	offset letter2	; 8 - x,y,letter,foreground,background
	dw	offset print	; 9 - x,y,VARPTR(message$),color
	dw	offset print2	; 10 - does nothing for now
	dw	offset sine	; 11 - x(in degrees),v(1) returns sin*10000
	dw	offset cosine	; 12 - x(in degrees),v(1) returns cos*10000
	dw	offset rnd	; 13 - if v(0)=0 randomize else v(1) rnd 1 to x
	dw	offset switch	; 14 - turn OFF lores, use mode at time of entry
	dw	offset lomode	; 15 - v(0)=IBM video mode else -1 for LORES.
	dw	offset lopaint	; 16 - x,y,fill,boundary

OUT_OF_RANGE	EQU	$-jump_table

;		 0   1	 2   3	 4   5	 6   7	 8   9	out 3d4h
video	db	71h,50h,5ah,0ah,7fh,06h,64h,70h,02h,01h ; out 3d5h
; this is the setup for LORES mode to the 6845 video controller.

seed	dw	41	; random number seed

boxed	dw	6 dup (0)  ; storage for BOX routine

vidmode db	0	; save ibm video mode on initialization
lostat	db	0	; is LORES active?
locolor db	0	; init=0, cls sets to value-used by print for scroll

first:
	call	first_init
	jmp	loret

plot:
	mov	dx,Y1
	mov	cx,X1
	mov	ax,X2
	sub	ah,ah
	call	plotdot
	jmp	loret

get:
	mov	dx,Y1
	mov	cx,X1
	call	getdot
	mov	X2,ax
	jmp	loret

line:
	push	si
	call	drawline
	jmp	loret

circle_setup:
	mov	ax,X1	   ; x
	push	ax
	mov	ax,Y1	   ; y
	push	ax
	mov	ax,X2	   ; radius
	or	ax,ax
	jz	cir_abort1 ; zero for radius leads to divide by zero error
	push	ax
	mov	ax,Y2	   ; aspect numerator
	or	ax,ax
	jz	cir_abort2 ; zero in aspect ratio is bogus too
	push	ax
	mov	ax,[si+8]  ; aspect denominator
	or	ax,ax
	jz	cir_abort3 ; so abort routine for safety.
	push	ax
	mov	ax,LEN	   ; color
	push	ax

	call	circle
	jmp	loret

cir_abort3:
	pop	ax	   ; safe circle exit for error conditions.
cir_abort2:
	pop	ax
cir_abort1:
	pop	ax
	pop	ax
	jmp	loret

letter1:
	mov	ax,Y2	; color
	mov	ah,al
	mov	bx,X2	; ascii value of letter
	mov	al,bl
	mov	dx,X1	; row (0-11)
	mov	dh,dl
	mov	bx,Y1	; col (0-19)
	mov	dl,bl

	call	letter	; print letter at pos. Ignor background
	jmp	loret

letter2:
	mov	ax,Y2	; color
	mov	ah,al
	mov	al,COLOR
	mov	cl,4
	shl	al,cl
	or	ah,al	; background
	mov	bx,X2	; letter
	mov	al,bl
	mov	dx,X1	; row
	mov	dh,dl
	mov	bx,Y1	; col
	mov	dl,bl

	call	slowletter  ; print letter in color,background - fill all dots.
	jmp	loret

print:
	mov	ax,Y2    ; color. if bit 7 set, print as xor
	mov	ah,al
	mov	dx,X1	; row (0-11)
	mov	dh,dl
	mov	bx,Y1	; col (0-19)
	mov	dl,bl
	mov	bx,X2	; varptr of string
	push	si
IF COMPILER
	mov	cx,[bx] ; compiler uses two bytes for string - max len=32767
	inc	bx
	inc	bx
	mov	si,[bx]

ELSE
	mov	cl,[bx] ; interpreter uses one byte.
	sub	ch,ch
	inc	bx
	mov	si,[bx]

ENDIF
	call	loprint ; this will figure out which way to print.
	pop	si	; get back v(0)
	sub	ax,ax
	mov	al,dh	; x
	mov	X1,ax
	mov	al,dl	; y
	mov	Y1,ax
	jmp	loret

print2:
	jmp	loret

lopaint:
	mov	cx,X1	; col
	mov	dx,Y1	; row
	mov	bx,X2	; fill color
	and	bl,7fh	; xor in paint leads to problems
	mov	ax,Y2	; boundary color
	mov	bh,al	; bh=bound,bl=fill
	mov	al,cl	; make one word since
	mov	ah,dl	; row=0-99,col=0-159. Save stack space.
	push	ax	; save on stack for first.

	call	lopaintr ; solve recursively
	jmp	loret	; back I hope.

sine:
	mov	ax,X1	; sin/cos use DEGREES, not radians
			; return result in v(1) as value*10000
sine1:
	cmp	ax,360	; scale to 0-319 range.
	jb	sine2
	sub	ax,360
	jmp	sine1

sine2:
	call	sin
	mov	Y1,ax	; return in v(1)
	jmp	loret

cosine:
	mov	ax,X1

cosine1:
	cmp	ax,360
	jb	cosine2
	sub	ax,360
	jmp	cosine1

cosine2:
	call	cos
	mov	Y1,ax
	jmp	loret

rnd:
	mov	ax,X1	; get number
	or	ax,ax
	jz	rnd1	; if its 0 then reseed generator
	call	random
	mov	Y1,ax	; else return rnd from 1 to x in array(1)
	jmp	loret

rnd1:
	call	randomize
	mov	ax,0ffffh  ; put -1 in array(0) to indicate done
	mov	X1,ax
	jmp	loret

lomode:
	call	lomo		; check for LORES active,
	jmp	loret		; else return IBM video mode.

lores	endp

lomo	proc	near
	mov	al,cs:lostat	; lostat set to -1 if lores active
	or	al,al
	jz	lomo1
	mov	ax,-1
	mov	X1,ax
	ret

lomo1:
	mov	ah,15		; if not active, return actual video state
	int	10h
	mov	cs:vidmode,al
	sub	ah,ah
	mov	X1,ax
	ret
lomo	endp

switch	proc	near
; turn off lores mode, restoring original screen mode. If on mono, do nothing.

	sub	al,al
	mov	cs:lostat,al	; indicate off
	mov	al,cs:vidmode
	cmp	al,7		; mono screen?
	jz	swit1		; don't do anything,
	sub	ah,ah		; else restore mode on init.
	push	bp
	push	si
	int	10h
	pop	si
	pop	bp
swit1:
	jmp	loret

switch	endp

first_init    proc    near
; set up screen
; no regs changed
	push	si
	push	dx
	push	cx
	push	bx
	push	ax
	mov	ah,15		; store current mode for later
	int	10h
	mov	cs:vidmode,al
	mov	dx,MODE
	mov	al,1		; turn off video for setup
	out	dx,al

	push	ds		; save to get horiz. sync from table
	mov	ax,abs0
	mov	ds,ax		; parm_ptr at 0:74h

	assume	ds:abs0
	lds	bx,parm_ptr
	add	bx,12h		; points to 80x25 synch
	mov	al,[bx] 	; get value
	pop	ds		; back to start value.
	assume	ds:nothing

	mov	si,offset cs:video
	mov	cs:[si+2],al	; store in our table

	mov	dx,REGISTER	; 6845 out 3d4 then out 3d5
	mov	cx,10		; set ten regs

finit_loop:
	mov	al,10
	sub	al,cl
	out	dx,al
	inc	dx
	mov	al,cs:[si]
	out	dx,al
	inc	si
	dec	dx

	loop	finit_loop


	mov	cx,1fffh
	mov	di,0
	mov	ax,00deh	; set to black
	cld
	rep	stosw

	mov	dx,MODE
	mov	al,9
	out	dx,al		; re-enable video, disable blink

	mov	al,0ffh
	mov	cs:lostat,al	; indicate on
	sub	al,al
	mov	cs:locolor,al	; color 0 (black)

	pop	ax
	pop	bx
	pop	cx
	pop	dx
	pop	si		; restore-return
	ret

first_init    endp

cls	proc	near
; CLS to color in AL
; AX changed, others preserved

	mov	ax,[si] 	; color
	mov	cs:locolor,al	; store for later
	and	cs:locolor,7fh	; strip off xor bit
	push	di
	push	dx
	push	cx		; save regs

	test	al,80h		; want an xor?
	jz	cls10
	call	cls_xor
	jmp	cls20

cls10:
	push	ax		; save color
	mov	dx,MODE
	mov	al,1		; turn off video for setup
	out	dx,al

	mov	cx,1fffh
	mov	di,0
	pop	ax		; get color spec
	and	al,0fh		; only 0-15
	mov	ah,al		; copy
	shl	al,1		; mov lo 4 bits to high
	shl	al,1
	shl	al,1
	shl	al,1
	or	ah,al		; same color to each dot

	mov	al,0deh        ; character for setup
	cld
	rep	stosw

	mov	dx,MODE
	mov	al,9
	out	dx,al		; re-enable video, disable blink

cls20:
	pop	cx		; restore regs
	pop	dx
	pop	di
	jmp	loret		; back to caller

cls_xor:
	push	ds
	push	es
	pop	ds		; use ds to avoid seg override.
	push	bx
	and	al,0fh		; only 0-15
	mov	di,1		; first attribute pos.
	mov	bl,al		; copy color to bl
	mov	cl,4
	shl	bl,cl		; shift lo4 bits to hi
	or	bl,al		; get back orig. bl makes two dots.
	mov	cx,8000 	; attributes
	mov	dx,STATUS

clxr10:
	in	al,dx		; wait for horiz. retrace
	test	al,1		; for action
	jnz	clxr10
	cli

clxr20:
	in	al,dx
	test	al,1
	jz	clxr20
				; xor to memory takes too long for
	mov	ah,[di] 	; horiz. retrace window.
	sti			; interrupts ok.
	xor	ah,bl
clxr30:
	in	al,dx		; wait for horiz. retrace
	test	al,1		; for action
	jnz	clxr30
	cli

clxr40: 			; this takes longer, but makes no snow
	in	al,dx
	test	al,1
	jz	clxr40

	mov	[di],ah 	; back to screen
	sti
	inc	di		; get to next
	inc	di
	loop	clxr10		; do till done.

	pop	bx		; keep it neat
	pop	ds
	ret			; back to caller

cls	endp

plotdot proc	near
; set a dot to color
;    DX = row	( 0-99 )
;    CX = col	( 0-159 )
;    AL = color
; assumes ES points to video memory
; all regs preserved.
; plotdot mimics int 10h write dot, including xor dot
	push	dx
	push	si		; save regs
	push	cx
	push	bx
	push	ax
	cmp	dx,99		; don't do for out of range
	ja	pdbridge	; no jump takes less time
	cmp	cx,159
	ja	pdbridge
	push	ax		; save color
	mov	ax,dx		; copy row to ax
	shl	ax,1		; *2   row*160=
	shl	ax,1		; *4   row*128
	shl	ax,1		; *8
	shl	ax,1		; *16
	shl	ax,1		; *32
	shl	ax,1		; *64
	shl	ax,1		; *128
	shl	dx,1		; *2   + row*32
	shl	dx,1		; *4
	shl	dx,1		; *8   in 27 clocks
	shl	dx,1		; *16  instead of ~80
	shl	dx,1		; *32
	add	ax,dx		; row*128 + row*32
	mov	si,ax		; move to si
	add	si,cx		; + col
	or	si,1		; adjust for attribute
	pop	bx		; get back color

	mov	dx,STATUS

pdw1:
	in	al,dx
	test	al,1
	jnz	pdw1		; wait for horiz. retrace
	cli			; no more interrupts

pdw2:
	in	al,dx		; get status
	test	al,1		; is it high
	jz	pdw2		; wait till it is

	mov	bh,es:[si]	; get current byte
	sti			; interrupts ok
	test	cx,1		; check odd/even
	jz	pd1
	test	bl,80h		; check for xor
	jz	pdw2a		; no, jump
	mov	ch,bh		; save orig
	xor	bh,bl		; xor both
	and	bh,0fh		; mask out other dot
	and	ch,0f0h 	; mask our dot
	or	bh,ch		; combine
	jmp	short pdw3	; onward

pdbridge:
	jmp	pdret		; hop skip and a jump
pdw2a:
	and	bh,0f0h 	; its odd - set foreground
	and	bl,0fh		; filter bogus color
	or	bh,bl		; combine

pdw3:
	in	al,dx		; wait for horiz. retrace
	test	al,1
	jnz	pdw3
	cli

pdw4:
	in	al,dx
	test	al,1
	jz	pdw4

	mov	es:[si],bh	; back to screen
	sti
	jmp	short pdret

pd1:
	test	bl,80h		; check for xor
	jz	pd1a		; no-go on
	shl	bl,1		; shift to background
	shl	bl,1
	shl	bl,1
	shl	bl,1

	mov	ch,bh		; save orig
	xor	bh,bl		; xor both
	and	bh,0f0h 	; mask out other dot
	and	ch,0fh		; mask our dot
	or	bh,ch		; combine
	jmp	short pdw5	; onward

pd1a:
	and	bh,0fh		; save foreground
	shl	bl,1		; shift to background
	shl	bl,1
	shl	bl,1
	shl	bl,1
	or	bh,bl		; combine

pdw5:
	in	al,dx		; wait retrace to store on screen
	test	al,1
	jnz	pdw5
	cli

pdw6:
	in	al,dx
	test	al,1
	jz	pdw6
	mov	es:[si],bh
	sti

pdret:
	pop	ax
	pop	bx
	pop	cx
	pop	si
	pop	dx
	ret			; back to caller

plotdot endp

getdot	proc	near
; get a dot, return color
;    DX = row	( 0-99 )
;    CX = col	( 0-159 )
;    returns AX = color, or -1 if request out of range
; assumes ES points to video memory
	push	dx
	push	si		; save regs
	push	cx
	push	bx
	cmp	dx,99		; don't do for out of range
	ja	gdret_bad
	cmp	cx,159
	ja	gdret_bad
	mov	ax,dx
	mov	bl,160
	mul	bl		; 160 * row
	mov	si,ax
	add	si,cx		; + col
	or	si,1		; adjust for attribute

	mov	dx,STATUS

gdw1:
	in	al,dx
	test	al,1
	jnz	gdw1		; wait for horiz. retrace
	cli			; no more interrupts

gdw2:
	in	al,dx		; get status
	test	al,1		; is it high
	jz	gdw2		; wait till it is

	mov	al,es:[si]	; get current byte
	sti			; interrupts ok

	test	cl,1		; odd?
	jz	gd1
	and	al,0fh		; filter other dot
	sub	ah,ah
	jmp	short gdret	;back

gd1:
	mov	cl,4
	shr	al,cl		; hi dot to low
	sub	ah,ah
	jmp	short gdret

gdret_bad:
	mov	ax,0ffffh	; -1 = out of range

gdret:
	pop	bx
	pop	cx
	pop	si
	pop	dx
	ret


getdot	endp


;	DRAWLINE.ASM  - - as a near call
;	from Dr. Dobbs -- June 1983, p.75
;	Uses fast-line drawing technique from BYTE, Aug. 81
;
;	args in array parms
;	X1	Y1	X2	Y2	COLOR	LEN
;	push parms offset on stack, discarded on return
;	i.e. push address_of_first_array_element
;	don't expect it to be there on return
;	segments, bp preserved, others changed.

drawline proc	near
	push	bp
	mov	bp,sp
	mov	si,ARG1   ; i.e. V%(0)
	mov	bl,INC_X
	mov	ax,X2
	sub	ax,X1
	jge	dl1
	mov	bl,DEC_X
	neg	ax
dl1:
	mov	cx,ax
	mov	bh,INC_Y
	mov	ax,Y2
	sub	ax,Y1
	jge	dl2
	mov	bh,DEC_Y
	neg	ax
dl2:
	mov	dx,ax
	mov	di,offset cs:modify_base
	cmp	dx,cx
	jge	dl3
	xchg	cx,dx
	xchg	bl,bh
dl3:
	mov	ADJ_LONG_AXIS,bh
	mov	ADJ_MASTER,cx
	shr	cx,1
	mov	TEST_MASTER,cx
	mov	ALT_ADJ_MASTER,dx
	mov	ADJ_SHRT_AXIS,bl
	mov	di,dx
	cmp	LEN,0
	jle	dl4
	mov	di,LEN
dl4:
	mov	cx,X1
	mov	dx,Y1
	mov	al,COLOR
	sub	bx,bx
dl5:
	call	plotdot

modify_base	label	byte

	inc	cx
	add	bx,1111H
	cmp	bx,1111H
	jle	dl6
	sub	bx,1111H
	inc	dx
dl6:
	dec	di
	jge	dl5

	pop	bp
	ret	2
drawline endp

box	proc	near
; parms set up in boxed
; segments, bp preserved, others changed

	mov	di,offset boxed
	push	di
	push	si		; save for us
	push	es
	push	cs
	pop	es
	mov	cx,12		; get args to temp area
	cld
	rep	movsb
	pop	es
	pop	si
	pop	di
	mov	ax,cs:boxed	; bx1
	mov	X1,ax
	mov	ax,cs:boxed+2	; by1
	mov	Y1,ax
	mov	Y2,ax		; y2=y1
	mov	ax,cs:boxed+4	; bx2
	mov	X2,ax
	sub	ax,ax
	mov	LEN,ax		; length = whole

	push	si
	push	si		; store for drawline
	call	drawline

	pop	si
	push	si
	mov	ax,cs:boxed+4	; bx2
	mov	X1,ax
	mov	ax,cs:boxed+6	; by2
	mov	Y2,ax		; y2 (y1 ok from last)

	push	si
	call	drawline

	pop	si
	push	si
	mov	ax,cs:boxed+6	; by2
	mov	Y1,ax		; y1
	mov	ax,cs:boxed	; bx1
	mov	X2,ax		; x2

	push	si
	call	drawline

	pop	si
	push	si

	mov	ax,cs:boxed	; bx1
	mov	X1,ax		; x1
	mov	ax,cs:boxed+2	; by1
	mov	Y2,ax		; y2

	push	si
	call	drawline

	pop	di

	mov	si,offset boxed
	push	cs
	push	ds
	pop	es
	pop	ds

	mov	cx,12		; put args back in array
	cld
	rep	movsb

	mov	ax,es
	mov	ds,ax

	jmp	loret

box	endp

;-----------------------------------
; procedure
; circle(x,y,radius,number,denom,color:integer)
;
; Dan Lee  July 1, 1982
; SourceWare
;
; draws a circle at center (x,y) with aspect
; ratio numer/denom; radius in column units
;
; assumes entry via inter-segment call,
;	modified here as intra-segment
; frame:	value x 	: bp+16
;		value y 	: bp+14
;		value radius	: bp+12
;		value numer	: bp+10
;		value denom	: bp+8
;		value color	: bp+6
; segments, bp preserved, others changed
;--------------------------------------

circle	proc	near
	push	bp
	push	bp		; as substitute for near
	mov	bp,sp
	mov	ax,[bp+10]
	mov	bx,1024
	imul	bx
	mov	cx,[bp+8]
	idiv	cx
	push	ax
	xchg	ax,cx
	mov	cx,[bp+10]
	imul	bx
	idiv	cx
	mov	[bp+8],ax
	pop	ax
	mov	[bp+10],ax

; start by incrementing Y by one unit and
; decrementing X by TAN units*inv aspect
; start at (RADIUS,Y) and plot to 45 degrees

	mov	ax,[bp+12]
	mov	bx,1024
	imul	bx
	sub	di,di

cr5:
	push	ax
	push	dx
	sub	bx,bx
	add	ax,512
	adc	dx,bx
	mov	bx,1024
	idiv	bx
	mov	bx,ax
	add	ax,[bp+16]
	mov	dx,[bp+14]
	sub	dx,di
	mov	cx,ax
	mov	al,[bp+6]
	mov	ah,12
	call	plotdot
	sub	cx,bx
	sub	cx,bx
	call	plotdot
	add	dx,di
	add	dx,di
	call	plotdot
	add	cx,bx
	add	cx,bx
	call	plotdot

; cx now at original point

	xchg	cx,bx
	inc	di
	mov	ax,di
	mov	bx,[bp+8]
	imul	bx
	idiv	cx
	sub	dx,dx
	mov	si,ax
	idiv	bx
	cmp	ax,1
	pop	dx
	pop	ax
	jae	cr7
	neg	si
	mov	bx,-1
	add	ax,si
	adc	dx,bx
	jmp	short cr5

; plot 45 to 90 degrees
; now decrease X by one unit and
; increase Y by COT units*aspect ratio

cr7:
	mov	ax,di
	mov	bx,1024
	imul	bx
	mov	di,cx
	dec	di

cr8:
	push	ax
	push	dx
	sub	bx,bx
	add	ax,512
	adc	dx,bx
	mov	bx,1024
	idiv	bx
	mov	bx,ax
	add	ax,[bp+14]
	mov	cx,[bp+16]
	add	cx,di
	mov	dx,ax
	mov	al,[bp+6]
	mov	ah,12
	call	plotdot

	sub	cx,di
	sub	cx,di
	call	plotdot
	sub	dx,bx
	sub	dx,bx
	call	plotdot
	add	cx,di
	add	cx,di
	call	plotdot
	sub	dx,[bp+14]
	neg	dx
	xchg	cx,dx
	or	di,di
	js	cr11
	dec	di
	mov	ax,di
	mov	bx,[bp+10]
	imul	bx
	idiv	cx
	mov	si,ax

	pop	dx
	pop	ax
	sub	bx,bx
	or	si,si
	jns	cr10
	mov	bx,-1

cr10:
	add	ax,si
	adc	dx,bx
	jmp	short cr8

; exit

cr11:
	add	sp,4
	pop	bp
	pop	bp
	ret	12

circle	endp

lopaintr proc	 near
; using boundary fill recursive algorithm
; from Fundamentals of Interactive Computer Graphics
; by J.D. Foley and A. Van Dam
; Addison-Wesley 1982, p. 450
; has one stack data as hi=row,lo=col
; BX has been set with color as BH=boundary,BL=fill color

	push	bp
	mov	bp,sp
	mov	cl,[bp+4]	; lo byte=col
	sub	ch,ch		; 0-159
	mov	dl,[bp+5]	; hi byte=row
	sub	dh,dh
	mov	si,cx		; save for left scan

scan_write_right:
	mov	al,bl		; fill color
	call	plotdot
	inc	cx
	call	getdot
	cmp	al,bl		; is it fill color?
	jz	lpr10
	cmp	al,bh		; is it boundary color?
	jz	lpr10
	cmp	al,0ffh 	; is it out of range?
	jz	lpr10
	jmp	scan_write_right ; no, keep going

lpr10:
	dec	cx
	xchg	si,cx		; save rightmost for later

scan_write_left:
	mov	al,bl
	call	plotdot
	dec	cx
	call	getdot
	cmp	al,bl		; is it fill color?
	jz	lpr20
	cmp	al,bh		; is it boundary color?
	jz	lpr20
	cmp	al,0ffh 	; is it out of range?
	jz	lpr20
	jmp	scan_write_left ; no, keep going

lpr20:
	inc	cx
	mov	di,cx		; save leftmost

	dec	dx		; up one line
	cmp	dx,0		; is less than 0?
	jl	find_down_right
	mov	cx,si		; recover right

find_up_right:
	cmp	cx,di		; end of scan?
	jl	find_down_right
	call	getdot
	cmp	al,bl		; fill color?
	jz	lpr40		; scan for start
	cmp	al,bh		; boundary color?
	jz	lpr40
lpr25:				; if not fill/bound, stack it
	mov	ah,dl		; row to hi
	mov	al,cl		; col to lo
	push	ax		; its a start, save on stack

lpr30:				; scan for boundary/fill
	dec	cx
	cmp	cx,di		; end of scan?
	jl	find_down_right
	call	getdot
	cmp	al,bl		; is it fill?
	jz	lpr40
	cmp	al,bh		; or boundary?
	jz	lpr40		; if so, look for start
	jmp	lpr30		; else continue

lpr40:
	dec	cx
	cmp	cx,di
	jl	find_down_right
	call	getdot
	cmp	al,bl		; is it fill?
	jz	lpr40
	cmp	al,bh		; or boundary?
	jz	lpr40		; if so continue scan
	jmp	lpr25		; its a start

find_down_right:
	mov	cx,si		; recover right
	inc	dx
	inc	dx		; scan row below
	cmp	dx,99
	ja	do_while

	cmp	cx,di		; end of scan?
	jl	do_while
	call	getdot
	cmp	al,bl		; fill color?
	jz	lpr60		; scan for start
	cmp	al,bh		; boundary color?
	jz	lpr60
lpr45:
	mov	ah,dl		; row to hi
	mov	al,cl		; col to lo
	push	ax		; its a start, save on stack

lpr50:				; scan for boundary/fill
	dec	cx
	cmp	cx,di		; end of scan?
	jl	do_while
	call	getdot
	cmp	al,bl		; is it fill?
	jz	lpr60
	cmp	al,bh		; or boundary?
	jz	lpr60		; if so, look for start
	jmp	lpr50		; else continue

lpr60:
	dec	cx
	cmp	cx,di
	jl	do_while
	call	getdot
	cmp	al,bl		; is it fill?
	jz	lpr60
	cmp	al,bh		; or boundary?
	jz	lpr60		; if so continue scan
	jmp	lpr45		; its a start

do_while:
	cmp	sp,bp		; any pushed addresses?
	jae	lprret		; sp=bp means no work pending
				; sp>bp means trouble
	call	lopaintr	; resolve stacked right addresses
	jmp	do_while	; then check again

lprret:
	pop	bp		; recover last frame
	ret	2		; discard data

lopaintr	endp

random	proc	near
; return random number from 1 to ax
; AX changed to random, other regs. preserved

	push	dx
	push	cx
	push	bx
	push	ax		; rnd request

	call	rn1		; AX has seed
	pop	cx		; get back request
	mov	dx,0		; only 16 bit
	idiv	cx		; divide
	xchg	ax,dx		; just want remainder
	inc	ax		; 1 to AX
	pop	bx
	pop	cx
	pop	dx

	ret			; back to caller

rn1:
	mov	ax,cs:seed	; seed @
	mov	cx,MULT 	; MULT
	mov	dx,0		; clear out msw
	imul	cx		; M*
	mov	cx,MODULUS	; MODULUS
	idiv	cx		; M/MOD

	mov	cs:seed,dx	; seed !
	xchg	ax,dx		; set up for MOD

	ret

randomize:
; reseed random number generator
; no regs changed
	push	ax
	push	cx
	push	dx

	sub	ah,ah
	int	1ah		; time of day
	sub	dh,dh		; DX has low word
	xchg	ax,dx
	mov	cx,100
	div	cl		; only 8 bits needed
	mov	cl,ah		; get remainder
	inc	cx

rn2:
	push	cx
	call	rn1
	pop	cx

	loop	rn2

	pop	dx
	pop	cx
	pop	ax

	ret

random	endp

letter	proc	near
; prints letter in al with color in ah
; at location specied in dl,dh
; There are 12 lines of 20 characters,
; DH = row = 0-11, DL = col = 0-19.

; first calculate screen position for top left dot.
; translate DX to screen coordinates.

; This is fast version, only sets dots that are on.
; use slowletter for background other than current.

	push	ds	; only AX lost
	push	si
	push	dx
	push	cx
	push	bx

	push	ax	; save letter and color
	mov	al,dh	; row
	cbw
	shl	ax,1
	shl	ax,1	; 8 dots verticle and horizontal
	shl	ax,1	; times rows
	mov	dh,al	; store screen pos. in dh - 8 bits ok
	mov	al,dl	; col
	cbw
	shl	ax,1
	shl	ax,1
	shl	ax,1	; for columns
	mov	dl,al	; store screen pos. in dl
	mov	ax,0f000h
	mov	ds,ax	; rom table
	mov	si,ROM_TABLE_ADDRESS
	pop	cx	; get back letter and color
	mov	al,cl	; 8 bytes per character
	cbw
	shl	ax,1
	shl	ax,1
	shl	ax,1	; times character
	add	si,ax	; offset into table
	mov	al,ch	; color
	mov	bl,ch	; store here too
	mov	cx,8	; eight shows up a lot

let1:
	push	dx	; save screen pos.
	mov	ah,[si] ; get bit-pattern
	mov	bh,80h	; mask


let2:
	test	ah,bh	; check for dot
	jz	let4	; if no dot skip

let3:
	push	dx
	push	cx
	mov	cl,dl	; col
	mov	ch,0
	mov	dl,dh	; row
	mov	dh,0
	call	plotdot
	pop	cx
	pop	dx

let4:
	shr	bh,1	; mask
	jc	let5	; when bit drops out we're done
	inc	dl	; set to next dot to right
	jmp	let2

let5:
	pop	dx	; get back orig.
	inc	dh	; set to next row.
	inc	si	; set to next byte.

	loop	let1	; do for whole matrix (8x8)

	pop	bx	; restore regs.
	pop	cx
	pop	dx
	pop	si
	pop	ds

	ret		; back to caller

letter	endp


slowletter  proc    near
; prints letter in al with color in ah
; at location specied in dl,dh
; There are 12 lines of 20 characters,
; DH = row = 0-11, DL = col = 0-19.

; first calculate screen position for top left dot.
; translate DX to screen coordinates.

	push	ds	; only AX lost
	push	si
	push	dx
	push	cx
	push	bx

	push	ax	; save letter and color
	mov	al,dh	; row
	cbw
	shl	ax,1
	shl	ax,1	; 8 dots verticle and horizontal
	shl	ax,1	; times rows
	mov	dh,al	; store screen pos. in dh - 8 bits ok
	mov	al,dl	; col
	cbw
	shl	ax,1
	shl	ax,1
	shl	ax,1	; for columns
	mov	dl,al	; store screen pos. in dl
	mov	ax,0f000h
	mov	ds,ax	; rom table
	mov	si,ROM_TABLE_ADDRESS
	pop	cx	; get back letter and color
	mov	al,cl	; 8 bytes per character
	cbw
	shl	ax,1
	shl	ax,1
	shl	ax,1	; times character
	add	si,ax	; offset into table
	mov	al,ch	; color
	mov	bl,ch	; store here too
	mov	cx,8	; eight shows up a lot

slet1:
	push	dx	; save screen pos.
	mov	ah,[si] ; get bit-pattern
	mov	bh,80h	; mask


slet2:
	test	ah,bh	; check for dot
	jnz	slet3	; if dot skip
	push	dx	; code here will set black dots
	push	cx	; in space where there should be no dots.
	call	bgrnd	; get background color for plotdot
	mov	cl,dl	; col
	sub	ch,ch
	mov	dl,dh	; row
	sub	dh,dh
	call	plotdot ; set to black
	pop	cx
	pop	dx
	mov	al,bl	; get back color
	jmp	slet4

slet3:
	push	dx
	push	cx
	mov	cl,dl	; col
	sub	ch,ch
	mov	dl,dh	; row
	sub	dh,dh
	call	plotdot
	pop	cx
	pop	dx

slet4:
	shr	bh,1	; mask
	jc	slet5	; when bit drops out we're done
	inc	dl	; set to next dot to right
	jmp	slet2

slet5:
	pop	dx	; get back orig.
	inc	dh	; set to next row.
	inc	si	; set to next byte.

	loop	slet1	; do for whole matrix (8x8)

	pop	bx	; restore regs.
	pop	cx
	pop	dx
	pop	si
	pop	ds

	ret		; back to caller

bgrnd:
; make a background out of al, account for xor
	test	al,80h
	jnz	bgrnd1
	shr	al,1
	shr	al,1
	shr	al,1
	shr	al,1	; move to foreground dot

	ret

bgrnd1:
	shr	al,1
	shr	al,1
	shr	al,1
	shr	al,1
	or	al,80h

	ret

slowletter  endp

loprint proc	near
; print a BASIC string - CX has length.
; at DH=row, DL=col, AH=color, DS:SI point to string
; returns with DH,DL containing next locate position
; AX, SI, CX, DX changed. Others OK.

	cmp	dh,11
	ja	lopret	; reject out of range
	cmp	dl,19
	ja	lopret
	or	cx,cx	; EOS?
	jz	lopret
	test	ah,0f0h ; using backgrounds?
	jnz	lop3

lop1:
	mov	al,[si] ; get char
	cmp	al,13	; CR?
	jz	lop1c
	cmp	al,8	; BS?
	jnz	lop1b
	call	back_space
	jmp	short lop2
lop1b:
	push	ax	; color save
	call	letter
	pop	ax

	inc	dl
	cmp	dl,20	; too far over?
	jnz	lop2
lop1c:
	inc	dh
	cmp	dh,12	; too far down?
	jnz	lop1a	; scroll screen
	call	loscroll
	mov	dh,11
lop1a:
	mov	dl,0	; set to start of next line

lop2:
	inc	si

	loop	lop1

lopret:
	ret

lop3:
	mov	al,[si] ; get char
	cmp	al,13	; CR?
	jz	lop3c
	cmp	al,8	; BS?
	jnz	lop3b
	call	back_space
	jmp	short lop4
lop3b:

	push	ax	; color save
	call	slowletter
	pop	ax

	inc	dl
	cmp	dl,20	; too far over?
	jnz	lop4
lop3c:
	inc	dh
	cmp	dh,12	; too far down?
	jnz	lop3a	; scroll screen
	call	loscroll
	mov	dh,11
lop3a:
	mov	dl,0	; set to start of next line

lop4:
	inc	si
	loop	lop3
	jmp	lopret

loprint endp

back_space	proc	near
	push	ax
	push	cx
	or	dl,dl	; at first col?
	jnz	bs1
	or	dh,dh	; at first row?
	jz	bsret	; nothing to do.
	dec	dh	; else set to one row up
	mov	dl,20	; and last col+1

bs1:
	dec	dl	; one space back
	mov	al,' '  ; make a space
	mov	ah,cs:locolor	; background color
	mov	cl,4	; move to bkgrnd
	shl	ah,cl
	call	slowletter	; make it

bsret:
	pop	cx
	pop	ax
	ret

back_space	endp

loscroll	proc	near
; scroll lores screen 1 row, use value in locolor for blank row
	push	ax
	push	bx
	push	cx
	push	dx
	push	si

	push	ds
	push	es
	pop	ds	; es,ds point to video
	mov	si,1280 ; row 1, col 0
	mov	di,0	; row 0, col 0
	mov	cx,14720 ; 160 bytes/row * 92 rows

	mov	dx,MODE
	mov	al,1
	out	dx,al	; turn off video or it will start snowing

	cld
	rep	movsb	; do scroll
	mov	ah,cs:locolor	; use last cls or init for blank line
	and	ah,0fh	;
	mov	bl,ah
	mov	cl,4
	shl	bl,cl	; move color into both halves of attribute
	or	ah,bl	; combine
	mov	al,0deh ; char 222
	mov	di,14080 ; row 88, col 0
	mov	cx,960
	cld
	rep	stosw	; store ax in screen[di]

	mov	dx,MODE
	mov	al,9
	out	dx,al	; turn on video

	pop	ds	; get back BASIC info

	pop	si
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	ret

loscroll	endp

; trig lookup functions
; from Dr. Dobbs - Oct. 82 p.53
; by Ray Duncan from public domain FORTH
; program by John James

trig_lookup	proc	near

trig:
	mov	bx,ax
	cmp	bx,90
	jle	trig1
	sub	bx,180
	neg	bx

trig1:
	sal	bx,1
	mov	ax,cs:sintbl[bx]
	ret

cos:
	add	ax,90

sin:
	push	dx
	push	bx
	cwd
	mov	bx,360
	idiv	bx
	mov	ax,dx
	or	ax,ax
	jns	sin2
	add	ax,360

sin2:
	cmp	ax,180
	jle	sin3
	sub	ax,180
	call	trig

	neg	ax
	jmp	sin4

sin3:
	call	trig

sin4:
	pop	bx
	pop	dx
	ret		; to caller

sintbl	dw	0	; 0 degrees
	dw	175
	dw	349
	dw	523
	dw	698
	dw	872
	dw	1045
	dw	1219
	dw	1392
	dw	1564
	dw	1736	; 10
	dw	1908
	dw	2079
	dw	2250
	dw	2419
	dw	2588
	dw	2756
	dw	2924
	dw	3090
	dw	3256
	dw	3420	; 20
	dw	3584
	dw	3746
	dw	3907
	dw	4067
	dw	4226
	dw	4384
	dw	4540
	dw	4695
	dw	4848
	dw	5000	; 30
	dw	5150
	dw	5299
	dw	5446
	dw	5592
	dw	5736
	dw	5878
	dw	6018
	dw	6157
	dw	6293
	dw	6428	; 40
	dw	6561
	dw	6691
	dw	6820
	dw	6947
	dw	7071
	dw	7193
	dw	7314
	dw	7431
	dw	7547
	dw	7660	; 50
	dw	7771
	dw	7880
	dw	7986
	dw	8090
	dw	8192
	dw	8290
	dw	8387
	dw	8480
	dw	8572
	dw	8660	; 60
	dw	8746
	dw	8829
	dw	8910
	dw	8988
	dw	9063
	dw	9135
	dw	9205
	dw	9272
	dw	9336
	dw	9397	; 70
	dw	9455
	dw	9511
	dw	9563
	dw	9613
	dw	9659
	dw	9703
	dw	9744
	dw	9781
	dw	9816
	dw	9848	; 80
	dw	9877
	dw	9903
	dw	9925
	dw	9945
	dw	9962
	dw	9976
	dw	9986
	dw	9994
	dw	9998
	dw	10000	; 90

trig_lookup	endp

lastword	db	0

cseg	ends
	end

                                                            