;
; BD Software C Standard Library Machine Language Functions
; Written by Leor Zolman
; v1.46,        3/15/82
;
; This file is in "CSM" format; to convert to CRL format,
; use CASM.SUB in conjunction with CASM.COM, ASM.COM and DDT.COM.
;
; Functions appearing in this file:
;
;       rread   rwrite  rtell   rseek   rsrec   rcfsiz
;       setjmp  longjmp
;       setplot clrplot line    plot    txtplot
;       index   getline
;
;
;
; The random-record file I/O function contained here are NOT documented
; in the User's Guide, because they are non-portable to pre-2.0 CP/M
; Systems.
;

       maclib  bds

;
; Here are the new random-access file I/O routines
; for use with CP/M version 2.x ONLY...these functions
; will NOT work under pre-2.x CP/M's.
;
; 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.
;

       FUNCTION 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
       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 is a pain.
       ora     a
       jz      r4      ;go to r4 if no problem
       cpi     1
       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
       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
       jmp     r2
       ENDFUNC

;
; 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)
;

       FUNCTION 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
       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
       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
       jmp     nwr2    ; and go try next sector
       ENDFUNC

;
; 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)
;

       FUNCTION rseek

       call    arghak
       lda     arg1
       call    fgfcb
       jc      error
       push    h
       call    rtell2
       lhld    arg2
       lda     arg3    ;is origin == 0?
       ora     a
       jz      rseek2  ;if so, HL holds new position
       dcr     a       ;no. is origin == 1?
       jnz     rseek1
       dad     d       ;yes. add offset to current position
       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
       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
       ENDFUNC

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

       FUNCTION 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
       ENDFUNC

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

       FUNCTION 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
       ENDFUNC

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

       FUNCTION        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
       ENDFUNC

       FUNCTION        setjmp
       call    ma1toh
       mov     m,c     ;save BC
       inx     h
       mov     m,b
       inx     h
       xchg
       lxi     h,0
       dad     sp
       xchg
       mov     m,e     ;save SP
       inx     h
       mov     m,d
       inx     h
       pop     d       ;save return address
       push    d
       mov     m,e
       inx     h
       mov     m,d
       lxi     h,0     ;and return 0
       ret
       ENDFUNC

       FUNCTION        longjmp
       call    ma1toh  ;get buffer address
       mov     c,m     ;restore BC
       inx     h
       mov     b,m
       inx     h
       mov     e,m     ;restore SP...first put it in DE
       inx     h
       mov     d,m
       inx     h
       shld    temp    ;save pointer to return address
       call    ma2toh  ;get return value
       xchg            ;put return val in DE, old SP in HL
       sphl            ;restore SP with old value
       pop     h       ;pop retur address off stack
       lhld    temp    ;get back ptr to return address
       mov     a,m
       inx     h
       mov     h,m
       mov     l,a     ;HL holds return address
       xchg            ;put ret addr in DE, get return value in HL
       push    d       ;push return address on stack
       ret             ;and return...
temp:   ds 2
       ENDFUNC


       FUNCTION        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
       ENDFUNC

       FUNCTION        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)
       jnz     clr2
       ret
       ENDFUNC

       FUNCTION        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
       call    put     ; put up one endpoint at BC
       lda     arg4    ;put other endpoint data in HL
       mov     c,a
       lda     arg5
       mov     b,a
       call    put     ;(but first put up the point from BC)
       mov     h,b
       mov     l,c
       call    liner   ;now connect them...
       pop     b
       ret             ;all done.

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

line2:  call    midp    ;find midpoint
       call    put     ;put it up
       push    d       ;set up recursive calls
       mov     d,b
       mov     e,c
       call    liner
       xthl
       call    liner
       xchg
       pop     h
       ret             ;and we are done!

midp:   push    h
       push    d

       mov     a,h
       sub     d
       ani     1
       jz      mid3

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

mid2a:  dcr     h

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

       mov     a,l
       cmp     e
       jc      mid3a
       inr     e
       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

put:    push    h
       push    d
       mov     a,b
       lhld    ysize
       xchg
       lhld    pbase
       inr     a
put1:   dcr     a
       jz       put2
       dad     d
       jmp     put1

put2:   mov     e,c
       mvi     d,0
       dad     d
       lda     arg1
       mov     m,a
       pop     d
       pop     h
       ret

abs:    ora     a
       rp
       cma
       inr     a
       ret
       ENDFUNC

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

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

       FUNCTION        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
       jz      txt2
       mvi     b,80h
txt2:   mov     a,m
       ora     a
       jnz     txt3
       pop     b
       ret

txt3:   ora     b
       stax    d
       inx     h
       inx     d
       jmp     txt2
       ENDFUNC

;
; Index(str,substr)
; char *str, *substr;
;
; Returns index of substr in str, or -1 if not found.
;

       FUNCTION        index
       call    arghak
       lhld    arg1
       xchg            ;main str ptr in DE
       lhld    arg2    ;substr ptr in HL
       dcx     d
index1: inx     d
       ldax    d       ;end of str?
       ora     a
       jnz     index2
       lxi     h,-1    ;yes. not found.
       ret
index2: cmp     m       ;quick check for dissimilarity
       jnz     index1  ;loop if not same right here
       push    d       ;else do long compare
       push    h
index3: inx     h
       inx     d
       mov     a,m     ;end of substr?
       ora     a
       jnz     index4  ;if not, go on testing
       pop     d       ;else matches
       pop     d       ;get starting address of substr in DE
       lhld    arg1    ;subtract beginning of str
       call    cmh
       dad     d       ;and return the result
       ret

index4: ldax    d       ;current char match?
       cmp     m
       jz      index3  ;if so, keep testing
       pop     h       ;else go on to next char in str
       pop     d
       jmp     index1
       ENDFUNC

;
; Getline(str,lim)
; char *str;
;
; Gets a line of text from the console, up to 'lim' characters.
;

       FUNCTION        getline
       push    b
       call    ma3toh  ;get max no. of chars
       mov     c,a     ;save in C
       call    ma2toh  ;get destination address
       push    h
       push    h
       lxi     h,-150  ;use space below stack for reading line
       dad     sp
       push    h       ;save buffer address
       mov     m,c     ;Set max # of characters
       mvi     c,getlin
       xchg            ;put buffer addr in DE
       call    bdos    ;get the input line
       mvi     c,conout
       mvi     e,lf    ;put out a LF
       call    bdos
       pop     h       ;get back buffer address
       inx     h       ;point to returned char count
       mov     b,m     ;set B equal to char count
       inx     h       ;HL points to first char of line
       pop     d       ;DE points to start destination area
copyl:  mov     a,b     ;copy line to start of buffer
       ora     a
       jz      gets2
       mov     a,m
       stax    d
       inx     h
       inx     d
       dcr     b
       jmp     copyl

gets2:  xra     a       ;store terminating null
       stax    d
       pop     h       ;return buffer address in HL
       pop     b
       ret
       ENDFUNC