title 'BDSNEW - New BDS C Library Functions' ; ; BDSNEW - New BDS C Library Functions ; ; File creation date: April 12, 1982 @ 15:33 by BDR ; Last revision date: new file ; ; Operating System: CP/M 2.x ; Software Revision: 1.0 ; ; ; This file replaces several of the BDS C library functions that were ; written in C. The functions, being written in assembly rather than ; C, are much faster than the old functions. The most impressive speed ; increase was found in the 'strcmp' function, which is used fairly often ; in many applications. ; ; These functions are believed to accurately replace the corresponding ; BDS C functions. If any bugs are found, please notify me either ; through Uucp (ucbvax(shriek)ucivax(shriek)csuf(shriek)bruce), or ; through the Garden Grove Data Exchange RCPM system: (714) 534-1547. ; (Gee, I wish MAC allowed exclamation marks in comments...) ; ; Bruce Robertson ; page ; ; Modification History ; ==================== ; ; July 21, 1982 Modified to include two block move functions MOVE and ; MOVDN which assemble with the Z80 move instructions if ; the 'z80' equate is true.(Incidently using conditionals ; with CASM isn't easy..it doesn't recognize 'if..endifs') ; J.R. ; July 19, 1982 Modified to be compatible with the CASM preprocessor. ; Jack Riley (303) 499-9169 RCPM ; ; v1.0 April 12, 1982 @ 13:33 by BDR ; Original file was created. ; page maclib bds page z80 equ 1 ; set to 1 only if code used on z80 computer ; ; This macro is used with the character argument functions to fetch ; the argument from the stack. The argument is placed into register A. ; getbyte macro lxi h,2 ;; load the offset to the argument dad sp ;; HL now points to the argument mov a,m ;; fetch the argument from the stack endm page ; ; Character functions: isupper(), islower(), isalpha(), isdigit(), ; toupper(), tolower(), isspace() ; ; Each of these functions expects a single 'char' as an argument. The ; functions starting with 'is' return either TRUE or FALSE for a given ; character argument, depending on the condition the function is testing. ; the functions starting with 'to' return a (possibly altered) character. ; ; ; Return TRUE if upper case. ; FUNCTION isupper getbyte ; fetch the argument from the stack lxi h,0 ; assume that it isn't upper case cpi 'A' ; can't be lower than an A... rc ; return with FALSE if it is cpi 'Z'+1 ; can't be greater than a Z... rnc ; return with FALSE if it is inr l ; change the result to TRUE ret ; return to caller ENDFUNC isupper ; ; Return TRUE if lower case. ; FUNCTION islower getbyte ; fetch the argument from the stack lxi h,0 ; assume that it isn't lower case cpi 'a' ; can't be lower than an 'a'... rc ; return with FALSE if it is cpi 'z'+1 ; can't be greater than a 'z'... rnc ; return with FALSE if it is inr l ; change the result to TRUE ret ; return to caller ENDFUNC islower ; ; Return TRUE if upper or lower case. ; FUNCTION isalpha getbyte ; fetch the argument from the stack lxi h,0 ; assume that it isn't alphabetic ani 0DFh ; if lower case, convert to upper case cpi 'A' ; can't be lower than an A... rc ; return with FALSE if it is cpi 'Z'+1 ; can't be larger than a Z... rnc ; return with FALSE if it is inr l ; change the result to TRUE ret ; return to caller ENDFUNC isalpha ; ; Return TRUE if digit. ; FUNCTION isdigit getbyte ; fetch the argument from the stack lxi h,0 ; assume that it isn't a digit cpi '0' ; can't be lower than a zero... rc ; return FALSE if it is cpi '9'+1 ; can't be larger than a nine... rnc ; return FALSE if it is inr l ; change the result to TRUE ret ; return to caller ENDFUNC isdigit ; ; Convert to upper case if lower case. ; FUNCTION toupper getbyte ; fetch the argument from the stack mov l,a ; assume that the character isn't lower case mvi h,0 ; (this is placing the character into HL) cpi 'a' ; can't be lower than an 'a'... rc ; return with the original character if it is cpi 'z'+1 ; can't be larger than a 'z'... rnc ; return with the original if it is ani 05Fh ; it's lower case - convert to upper case mov l,a ; put the new character into HL ret ; return to caller ENDFUNC toupper ; ; Convert upper case to lower case. ; FUNCTION tolower getbyte ; fetch the argument from the stack mov l,a ; assume that the character isn't upper case mvi h,0 ; (this is placing the character into HL) cpi 'A' ; can't be lower than an A... rc ; return with the original character if it is cpi 'Z'+1 ; can't be larger than a Z... rnc ; return with the original if it is ori 020h ; it's upper case - convert to lower case mov l,a ; put the new character into HL ret ; return to caller ENDFUNC tolower ; ; Return TRUE if character is a space, tab or newline. ; FUNCTION isspace getbyte ; fetch the argument from the stack lxi h,1 ; assume that it is a white space character cpi ' ' ; check for a space rz ; return if we have one cpi 'I'-040h ; check for a tab rz ; return if we have one cpi 'J'-040h ; check for a newline rz ; return if we have one dcr l ; make the result FALSE ret ; return to caller ENDFUNC isspace page ; ; Function: atoi() ; ; ; This function converts an ascii integer to its binary equivalent. White ; space at the front of the string is ignored, and a minus sign is ; recognized. ; ; int atoi(str) ; char *str; ; FUNCTION atoi pop h ; fetch the return address pop d ; fetch the pointer to the string push d ; put everything back the way it was push h push b ; save the BC register lxi h,0 ; zero the number accumulator xra a ; set the sign flag to POSITIVE push psw ; save the sign flag on the stack atoi10: ldax d ; fetch the next byte of the string inx d ; increment the string pointer cpi ' ' ; check for a leading space jz atoi10 ; go skip the space cpi 'I'-040h ; check for a leading tab jz atoi10 ; go skip the tab cpi '-' ; check for the minus sign jnz atoi20 ; jump if no minus sign pop psw ; fetch the sign flag inr a ; change the flag to NEGATIVE push psw ; put the sign flag back ldax d ; fetch the next string character inx d ; increment the pointer atoi20: sui '0' ; convert the alleged digit to binary cpi 10 ; it can't be larger than 9 jnc atoi30 ; jump if this isn't an ascii digit mov b,h ; copy the accumulator into BC mov c,l dad h ; multiply the accumulator by 8 dad h dad h dad b ; adding the original accumulator in twice... dad b ; multiplies the accumulator by 10. mov c,a ; put the current digit into BC mvi b,0 dad b ; add in the current digit ldax d ; fetch the next string character inx d ; increment the string pointer jmp atoi20 ; go check for another digit atoi30: pop a ; end of number - fetch the sign flag pop b ; restore the BC register rz ; return if the sign flag is POSITIVE mov a,h ; two's complement the accumulator cma mov h,a mov a,l cma mov l,a inx h ret ; return to caller ENDFUNC atoi page ; ; Function: strlen() ; ; ; This function returns the length in bytes of its string argument. The ; terminating zero byte is not counted in the length. ; ; unsigned strlen(str) ; char *str; ; FUNCTION strlen pop h ; fetch the return address... pop d ; fetch the string pointer push d ; put everything back the way it was push h mov h,d ; copy the string pointer into HL mov l,e slen10: ldax d ; fetch the next byte of the string inx d ; increment the string pointer ora a ; check for a zero byte jnz slen10 ; loop until we reach the end of the string ; ; To compute the length, we subtract the original string pointer from ; the new string pointer (which is pointing to the zero byte at the ; end of the string). ; dcx d ; we don't want to count the zero byte mov a,e ; subtract the low order bytes first sub l mov l,a mov a,d ; subtract the high order bytes next sbb h mov h,a ret ; return to caller with the string length ENDFUNC strlen page ; ; Function: strcmp() ; ; ; This function compares two strings. If the first argument is either ; longer than the second argument, or one if its bytes is arithmetically ; greater than the corresponding byte of the second argument (using the ; ASCII collating sequence), then a positive number is returned. If the ; second argument is greater than the first, a negative number is ; returned. If the strings are equal, zero is returned. ; ; If a positive or negative number is returned, the actual value returned ; is meaningless. Only the sign is of use. ; ; int strcmp(s1, s2) ; char *s1, *s2; ; FUNCTION strcmp call arghak ; hack apart the arguments lhld arg1 ; fetch the string 1 pointer xchg ; put it into DE lhld arg2 ; fetch the string 2 pointer scmp10: ldax d ; fetch the next byte of string 1 cmp m ; compare with the next byte of string 2 jnz scmp20 ; jump if the bytes are not equal inx h ; increment the string pointers inx d ora a ; if the byte is zero, we're all done jnz scmp10 ; loop if we're not done yet lxi h,0 ; return zero - the strings are equal ret ; return to caller scmp20: sub m ; figure out which byte (and string) is larger mov h,a ; return the result in HL ret ; return to caller ENDFUNC strcmp page ; ; Function: strcpy() ; ; ; This function copies the source string onto the destination string. If ; the destination string should happen to be valid, it will be destroyed. ; There MUST be enough room allocated for the destination string to hold ; the entire source string, or the code or data following the destination ; string will be overwritten. ; ; The function returns a pointer to the destination string. ; ; char *strcpy(dest, source) ; char *dest, *source; ; FUNCTION strcpy call arghak ; go hack apart the arguments lhld arg1 ; fetch the destination string pointer xchg ; put the pointer into DE lhld arg2 ; fetch the source string pointer scpy10: mov a,m ; fetch the next byte of the source string stax d ; store the byte into the destination string inx d ; increment the string pointers inx h ora a ; if A is zero, we've reached the string end jnz scpy10 ; loop if we're not at the end lhld arg1 ; return a pointer to the destination string ret ; return to caller ENDFUNC strcpy page ; ; Function: strcat() ; ; ; This function concatenates the source string onto the destination ; string. There MUST be enough room allocated at the end of the ; destination string to hold the entire source string. ; ; char *strcat(dest, source) ; char *dest, *source; ; FUNCTION strcat call arghak ; go hack apart the arguments lhld arg2 ; fetch the source string pointer xchg ; put the pointer into DE lhld arg1 ; fetch the destination string pointer ; ; The loop below finds the end of the destination string. ; scat10: mov a,m ; fetch the next byte of the destination string inx h ; increment the string pointer ora a ; if A is non-zero, we need to keep searching jnz scat10 ; loop until we reach the end of the string dcx h ; point back to the zero terminating byte ; ; Now we copy the source onto the end of the destination. ; scat20: ldax d ; fetch the next source string byte mov m,a ; store the byte into the destination string inx h ; increment the string pointers inx d ora a ; if A is zero, we've reached the end jnz scat20 ; loop if we have more to do lhld arg1 ; return a pointer to the destination string ret ; return to caller ENDFUNC strcat page ; ; Function: move() ; ; ; This function moves bytes from location to ; location . The move is performed from first to ; last bytes so if there is overlap of range, be sure that ; greater than . If this condition is not ; true use the 'movdn' function instead. The block move ; instruction is used if the Z80 equate is true. ; ; move(dest, source,length) ; char *dest, *source; ; int length FUNCTION move call arghak ; go hack apart the arguments lhld arg1 ; fetch the destination pointer xchg ; put the pointer into DE lhld arg3 ; fetch the length mov a,h ora l ; return on zero rz push h pop b ; put into BC lhld arg2 ; fetch the source pointer mve10: if z80 db 0edh,0b0h ; perform LDIR instruction ret ; return to caller else ; 8080 mov a,m ; fetch the next byte of the source string stax d ; store the byte into the destination string inx d ; increment the string pointers inx h dcx b ; decrement count mov a,b ; test zero in b ora c ; and c endif jnz mve10 ; loop if we're not at the end ret ; return to caller ENDFUNC move FUNCTION movdn page ; ; Function: movdn() ; ; ; This function moves bytes from location to ; location . The move is performed from last to ; first bytes so if there is overlap of range, be sure that ; less than . If this condition is not ; true use the 'move' function instead. Also use 'move' if ; there is no overlap since it is more efficient. The block move ; instruction is used if the Z80 equate is true. ; ; movdn(dest, source , length) ; char *dest, *source; ; int length call arghak ; go hack apart the arguments lhld arg3 ; fetch the length mov a,h ora l ; return on zero rz push h ; save xchg ; into DE lhld arg1 ; fetch the destination pointer dad d ; add length dcx h ; one too many push h lhld arg3 ; fetch the length xchg ; into DE lhld arg2 ; fetch the source pointer dad d ; add length dcx h ; one too many pop d ; get back destination pop b ; and length mvdn10: if z80 db 0edh,0b8h ; perform LDDR instruction ret ; return to caller else ; 8080 mov a,m ; fetch the next byte of the source string stax d ; store the byte into the destination string dcx d ; increment the string pointers dcx h dcx b ; decrement count mov a,b ; test zero in b ora c ; and c endif jnz mvdn10 ; loop if we're not at the end ret ; return to caller ENDFUNC movdn end  .