;
; More assembly-language BDS C library functions
; This file contains the DMA-video and CP/M 2.0 random-access
; file I/O routines.
;

	maclib bds
	maclib cmac

	direct
	define setplot		;define video board dimensions
	define clrplot		;clear the screen
	define line		;draw a crooked line
	define plot		;plot a point
	define txtplot		;put text up

	define rread
	define rwrite
	define rseek
	define rtell	
	define rsrec
	define rcfsiz

	enddir


;
; Setplot:
;
; Set up plotting paramters for the DMA video plotting
; routines:
;

	prelude setplot

	call arghak
	push b
 	lhld arg1		;get base address
	shld pbase		;	initialize
	lhld arg3		;get y size
	shld ysize		;	initialize
	xchg			;leave it in DE
	lhld arg2		;get x size
	shld xsize		;	initialize
	call usmul		;figure out screen size
	shld psize		;	initialize
	pop b
	ret

	postlude setplot

;
; Clrplot:
;
; Clear screen on DMA plotting device:
;

	prelude clrplot

	lhld psize		;put screen size
	xchg			;	in DE
	lhld pbase		;get screen base in HL
clr2:	mvi m,' '		;and
	inx h			;   clear
	dcx d			; 	each
	mov a,d			;	  location
	ora e			;		(all DE of 'em)
	reloc jnz,clr2
	ret

	postlude clrplot

;
; Line:
;
; This function draws a line between any 2 points on a device
; such as the VDM-1.
;
; The address of the VDM-1 screen is stored at pbase.
; This routine will only work for 16x64-orgranized video
; displays.
;

	prelude line

	call arghak		;get args
	push b
	lda arg2		;put one set of endpoint data in DE in
	mov c,a			;format:  D = x = arg2, E = y = arg3
	lda arg3
	mov b,a
	mov d,b
	mov e,c
	reloc call,put		; put up one endpoint at BC
	lda arg4		;put other endpoint data in HL
	mov c,a
	lda arg5
	mov b,a
	reloc call,put		;(but first put up the point from BC)
	mov h,b
	mov l,c
	reloc call,liner		;now connect them...
	pop b
	ret			;all done.


;
;  This routine draws a line between points specified by DE
;  and HL (it assumes that the two endpoints are already up
;  on the screen):
;

liner:	mov a,d
	sub h
	reloc call,abs
	cpi 2
	reloc jnc,line2		;are points far enough apart
				;in both dimensions to warrant
	mov a,e			;drawing a line?
	sub l
	reloc call,abs
	cpi 2
	reloc jnc,line2
	ret			;if not, return.


;
; Yes, the two points are sufficiently far apart to warrant a 
; line. So, draw it:
;

line2:	reloc call,midp		;find midpoint
	reloc call,put		;put it up
	push d			;set up recursive call
	mov d,b
	mov e,c
;
; Draw a line between one original point and the midpoint:
;

	reloc call,liner
	xthl
;
; Draw a line between the other original point and the midpoint:
; 	(Isn't recursion fun?)
;
	reloc call,liner
	xchg
	pop h
	ret			;and we are done!

;
; This routine finds the point as close as possible to the 
; midpoint between the two points specified in HL and DE.
; Coordinates of the midpoint are returned in BC:
;

midp:	push h
	push d

	mov a,h
	sub d
	ani 1
	reloc jz,mid3

	mov a,h
	cmp d
	reloc jc,mid2a
	inr d
	reloc jmp,mid3

mid2a:	dcr h

mid3:	mov a,l
	sub e
	ani 1
	reloc jz,mid4

	mov a,l
	cmp e
	reloc jc,mid3a
	inr e
	reloc jmp,mid4

mid3a:	dcr l

mid4:	mov a,h
	add d
	ora a
	rrc
	mov b,a
	mov a,l
	add e
	ora a
	rrc
	mov c,a
	pop d
	pop h
	ret

;
; Subroutine to put a point on the screen at the location
; specified by BC, where B=y,  C=x:
;

put:	push b
	push h
	lhld pbase
	mov a,c
	rrc
	rrc
	push psw
	ani 0c0h
	mov l,a
	pop psw
	ani 3
	add h
	mov h,a
	mov c,b
	mvi b,0
	dad b
	lda arg1
	mov m,a
	pop h
	pop b
	ret

;
; Subroutine to return A=abs(A):
;

abs:	ora a
	rp
	cma
	inr a
	ret

	postlude line		;end of "line" function


;
; Plot:
;
; Put a point on the DMA device:
;

	prelude plot

	call arghak
	lda arg1
	lhld ysize
	xchg
	lhld pbase
	inr a
plot1:	dcr a
	reloc jz,plotc
	dad d
	reloc jmp,plot1

plotc:	lda arg2
	mov e,a
	mvi d,0
	dad d
	lda arg3
	mov m,a
	ret

	postlude plot


;
; Txtplot:
;
; Put some text up on the DMA device:
;


	prelude txtplot

	call arghak
	push b
	lhld arg2
	xchg
	lhld ysize
	call usmul
	xchg
	lhld arg3
	dad d
	xchg
	lhld pbase
	dad d
	xchg
	lhld arg1
	mvi b,0
	lda arg4
	ora a
	reloc jz,txt2
	mvi b,80h
txt2:	mov a,m
	ora a
	reloc jnz,txt3
	pop b
	ret

txt3:	ora b
	stax d
	inx h
	inx d
	reloc jmp,txt2
	
	postlude txtplot



;
; Here are the new random-access file I/O routines
; for use with CP/M version 2.x...but not if you
; expect to use the generated programs on CP/M 1.4.
;
; the new functions are: rread, rwrite, rtell, rseek,
;			 rsrec, rcfsiz
;


;
; Rread:
;
; Read a number of sectors randomly
; Usage:
;
;	i = rread(fd, buf, n);
;
; The return value is either the number of sectors successfully
; read, 0 for EOF, or 1000 + (BDOS ERROR CODE)
; 
; The Random Record Field is incremented following each successful
; sector is read, just as if the normal (sequentail) read function
; were being used. Rseek must be used to go back to a previous 
; sector.
;

	prelude rread

	call arghak
	lda arg1
	call fgfd
	jc error
	mov a,m
	ani 2
	jz error
	push b
	lda arg1
	call fgfcb
	shld tmp2
	lxi h,0
	shld tmp2a
r2:	lhld arg3
	mov a,h
	ora l
	lhld tmp2a
	reloc jnz,r2a
	pop b
	ret

r2a:	lhld arg2
	xchg
	mvi c,sdma
	call bdos
	lhld tmp2
	xchg
	mvi c,readr	;code for BDOS random read
	push d		;save de so we can fudge nr field if
	call bdos	;we stop reading on extent boundary...
	pop d		; CP/M sucks!
	ora a
	reloc jz,r4		;go to r4 if no problem
	cpi 1
	reloc jz,r2b		;EOF?
	mov c,a		;put return error code in BC
	mvi b,0
	lxi h,1000	;add to 1000
	dad b
	pop b
	ret

r2b:	lxi h,32	;yes. are we on extent boundary?
	dad d
	mov a,m
	cpi 80h
	reloc jnz,r3
	mvi m,0		;yes. reset nr to 0...CP/M leaves it at 80!
r3:	lhld tmp2a	;(note: the above "bug" in CP/M was supposedly fixed
	pop b		; for 2.x, but one can never be sure...)
	ret

r4:	lhld arg3
	dcx h
	shld arg3
	lhld arg2
	lxi d,128
	dad d
	shld arg2
	lhld tmp2a
	inx h
	shld tmp2a
	lhld tmp2	;get address of fcb
	lxi b,33	;get addr of random record field
	dad b
	mov c,m		;bump
	inx h		;    value
	mov b,m		;	  of 
	inx b		;	    random
	mov m,b		;		  field
	dcx h		;			by one
	mov m,c
	reloc jmp,r2

	postlude rread


;
; Rwrite:
;
; The random "write" routine, which always copies the sector
; to be written down to tbuff before writing. Returns
; the # of sectors successfully written, or -1 on hard error.
; (the "1000 + error code" business is not used for rwrite)
;

	prelude rwrite

	call arghak
	lda arg1
	call fgfd
	jc error
	mov a,m
	ani 4
	jz error
	push b
	lda arg1
	call fgfcb
	shld tmp2
	lxi h,0
	shld tmp2a
	lxi d,tbuff 	;80 for normal CP/M, else 4280
	mvi c,sdma
	call bdos

nwr2:	lhld arg3	;done yet?
	mov a,h
	ora l
	lhld tmp2a	;if so, return count
	reloc jnz,nwr2a
	pop b
	ret

nwr2a:	lhld arg2	;else copy next 128 bytes down to tbuff
	lxi d,tbuff	;80 for normal CP/M, else 4280
	mvi b,128
nwr3:	mov a,m
	stax d
	inx h
	inx d
	dcr b
	reloc jnz,nwr3
	shld arg2	;save -> to next 128 bytes
	lhld tmp2	;get addr of fcb
	xchg
	mvi c,writr	;go write randomly
	call bdos
	ora a		;error?
	lhld tmp2a	;if so, return # of successfully written
	pop b		;  sectors.
	rnz
	push b
		
	inx h		; else bump successful sector count,
	shld tmp2a
	lhld arg3	; debump countdown,
	dcx h
	shld arg3
	lhld tmp2	; get address of fcb
	lxi b,33	; get address of random field
	dad b
	mov c,m		; bump 16-bit value at random
	inx h		; record
	mov b,m		;	field
	inx b		;	     of
	mov m,b		;	       fcb
	dcx h		;		  by one
	mov m,c
	reloc jmp,nwr2	; and go try next sector

	postlude rwrite


;
; rseek:
;
; rseek(fd, offset, origin)
;	   seeks to offset records if origin == 0,
;     to present position + offset if origin == 1,
;	or to end of file + offset if origin == 2.
; (note that in the last case, the offset must be non-positive)
;

	prelude rseek

	call arghak
	lda arg1
	call fgfcb
	jc error
	push h
	reloc call,rtell2
	lhld arg2
	lda arg3	;is origin == 0?
	ora a
	reloc jz,rseek2	;if so, HL holds new position
	dcr a		;no. is origin == 1?
	reloc jnz,rseek1
	dad d		;yes. add offset to current position
	reloc jmp,rseek2	;and result is in HL

rseek1:	pop d		;else origin must be 2...
	push d
	push b
	mvi c,cfsizc	;compute end of file position
	call bdos
	pop b
	pop h		;get back fcb
	push h
	reloc call,rtell2	;get DE = position
	lhld arg2	;add offset
	dad d		;and HL holds new position
rseek2:	xthl		;get fcb, push new position
	lxi d,33
	dad d		;HL points to random field of fcb
	pop d		;get new position in DE
	mov m,e		;and put into fcb
	inx h
	mov m,d
	xchg		;and return the position value
	ret

rtell2:	lxi d,33
	dad d
	mov e,m	
	inx h
	mov d,m
	ret

	postlude rseek


;
; Rtell:
;
; Return random record position of file:
;

	prelude rtell

	call arghak
	lda arg1
	call fgfcb
	jc error
	lxi d,33	;go to random record field
	dad d
	mov e,m		;get value into DE
	inx h
	mov d,m
	xchg		;put into HL
	ret

	postlude rtell


;
; Rsrec:
;
; Set random field from serial access mode:
;

	prelude rsrec

	call arghak
	lda arg1
	call fgfcb
	jc error
	push h
	xchg
	push b
	mvi c,srrecc
	call bdos
	pop b
	pop h
	lxi d,33
	dad d
	mov a,m
	inx h
	mov h,m
	mov l,a
	ret

	postlude rsrec


;
; Rcfsiz:
;
; set random record field to end-of-file:
;

	prelude rcfsiz

	call arghak
	lda arg1
	call fgfcb
	jc error
	push h
	xchg
	push b
	mvi c,cfsizc
	call bdos
	pop b
	pop h
	lxi d,33
	dad d
	mov a,m
	inx h
	mov h,m
	mov l,a
	ret

	postlude rcfsiz


