keep obj/stdlib mcopy stdlib.macros case on **************************************************************** * * StdLib - Standard Library Utility Functions * * This code implements the tables and subroutines needed to * support the standard C library STDLIB. * * December 1988 * Mike Westerfield * * Copyright 1988 * Byte Works, Inc. * * Note: Portions of this library appear in SysFloat * **************************************************************** * StdLib start dummy segment copy equates.asm end **************************************************************** * * void abort() * * Stop the program. * **************************************************************** * abort start ph2 #SIGABRT jsl raise lda #-1 brl ~C_QUIT end **************************************************************** * * int abs(int i) * * Return the absolute value of i. * * Inputs: * i - argument * * Outputs: * Returns abs(i). * **************************************************************** * abs start i equ 4 position of argument on stack lda i,S A := i bpl lb1 if A < 0 then eor #$FFFF A := -A inc A lb1 tay return A lda 2,S sta 4,S pla sta 1,S tya rtl end **************************************************************** * * void *aligned_alloc(size_t alignment, size_t size) * * Allocate memory with specified alignment. * * Inputs: * alignment - alignment to use (only value allowed is 1) * size - bytes of memory to allocate * * Outputs: * Returns pointer to allocated memory, or NULL on error. * **************************************************************** * aligned_alloc start csubroutine (4:alignment,4:size),0 lda alignment check that alignment==1 dec a ora alignment+2 beq good stz size return NULL on error stz size+2 lda #EINVAL sta >errno bra ret good ph4 ~EXITLIST sta [ptr] lda >~EXITLIST+2 sta [ptr],Y lda ptr sta >~EXITLIST lda ptr+2 sta >~EXITLIST+2 iny place the function address in the record iny lda func sta [ptr],Y iny iny lda func+2 sta [ptr],Y inc rval success... lb1 creturn 2:rval end **************************************************************** * * int at_quick_exit(func) * void (*func)(); * * This function is used to build a list of functions that will * be called as part of the quick exit processing. * * Inputs: * func - address of the function to call on quick exit * * Outputs: * Returns 0 if successful, -1 if not. * **************************************************************** * at_quick_exit start ptr equ 1 work pointer rval equ 5 return value csubroutine (4:func),6 lda #-1 assume we will fail sta rval assume we will fail dec4 func we need the addr-1, not the addr ph4 #8 get space for the record jsl malloc stx ptr+2 sta ptr ora ptr+2 quit now if we failed beq lb1 ldy #2 place the record in the exit list lda >~QUICKEXITLIST sta [ptr] lda >~QUICKEXITLIST+2 sta [ptr],Y lda ptr sta >~QUICKEXITLIST lda ptr+2 sta >~QUICKEXITLIST+2 iny place the function address in the record iny lda func sta [ptr],Y iny iny lda func+2 sta [ptr],Y inc rval success... lb1 creturn 2:rval end **************************************************************** * * atof - convert a string to a float * * Inputs: * str - pointer to the string * * Outputs: * X-A - pointer to converted number * **************************************************************** * atof start ph4 #0 no pointer returned lda 10,S pass the string addr on pha lda 10,S pha jsl strtod convert the string tay fix the stack lda 2,S sta 6,S pla sta 3,S pla tya rtl end **************************************************************** * * atoi - convert a string to an int * atol - convert a string to a long * * Inputs: * str - pointer to the string * * Outputs: * X-A - converted number * **************************************************************** * atoi start atol entry ph2 #10 base 10 ph4 #0 no pointer returned lda 12,S pass the string addr on pha lda 12,S pha jsl strtol convert the string tay fix the stack lda 2,S sta 6,S pla sta 3,S pla tya rtl end **************************************************************** * * atoll - convert a string to a long long * * Inputs: * str - pointer to the string * * Outputs: * converted number * **************************************************************** * atoll start ph2 #10 base 10 ph4 #0 no pointer returned lda 12,S pass the string addr on pha lda 12,S pha note: x reg is unchanged jsl strtoll convert the string lda 2,S fix the stack sta 6,S pla sta 3,S pla rtl end **************************************************************** * * char *bsearch(key, base, count, size, compar) * void *key, *base; * size_t count, size; * int (*compar)(const void *, const void *) * * Inputs: * key - pointer to element to search for * base - start address of the array to search * count - # elements in the array * size - size of each array element * compar - function that compares array elements * * Outputs: * Returns a pointer to the array element found; NULL if * no match was found. * **************************************************************** * bsearch start left equ 1 left index right equ 5 right index test equ 9 test index addr equ 13 address of array element of index test csubroutine (4:key,4:base,4:count,4:size,4:compar),16 lda count if count is 0 then ora count+2 jeq lb5 just return a null pointer lda compar patch the call address sta >jsl+1 lda compar+1 sta >jsl+2 stz left left = 0 stz left+2 sub4 count,#1,right right = count-1 lb1 clc test = (left+right)/2 lda left adc right sta test lda left+2 adc right+2 lsr A sta test+2 ror test mul4 test,size,addr addr = test*size + base add4 addr,base ph4 *addr then add4 test,#1,left left = test+1 bra lb3 else lb2 sub4 test,#1,right right = test-1 lb3 lda right+2 loop if right >= left bmi lb5 cmp left+2 bne lb4 lda right cmp left lb4 bge lb1 lb5 stz addr no match - return null stz addr+2 lb6 creturn 4:addr end **************************************************************** * * div_t div(n,d) * int n,d; * * Inputs: * n - numerator * d - denominator * * Outputs: * div_t - contains result & remainder * **************************************************************** * div start addr equ 1 csubroutine (2:n,2:d),4 phb use local data phk plb lda n do the divide ldx d jsl ~DIV2 sta div_t save the results stx div_t+2 lda n if the numerator is negative then bpl lb1 sub2 #0,div_t+2,div_t+2 make the remainder negative lb1 lla addr,div_t return the address plb creturn 4:addr div_t ds 4 end **************************************************************** * * void exit(int status); * * void _exit(int status); * * void _Exit(int status); * * void quick_exit(int status); * * Stop the program. Exit cleans up, _exit does not. Status * is the status returned to the shell. * * Inputs: * status - exit code * **************************************************************** * exit start jsr ~EXIT _exit entry _Exit entry lda 4,S brl ~C_QUIT end quick_exit start jsr ~QUICKEXIT lda 4,S brl ~C_QUIT end **************************************************************** * * char *getenv(const char *name) * * Returns a pointer to a shell variable. If the shell variable * has no value, a null is returned. * * Inputs: * namePtr - pointer to the name of the shell variable * * Outputs: * Returns a pointer to the shell variable * **************************************************************** * getenv start ptr equ 1 pointer to the shell variable csubroutine (4:namePtr),4 phb use local addressing phk plb lla ptr,0 initialize the pointer to null short I,M copy the variable name to the buffer ldy #0 lb1 lda [namePtr],Y beq lb2 iny sta name,Y bne lb1 dey lb2 sty name long I,M Read_Variable rdRec read the shell variable bcs lb3 if there was no error then lda var if the variable was set then and #$00FF beq lb3 short I,M set the null terminator ldx var stz var+1,X long I,M lla ptr,var+1 set the pointer to return lb3 plb restore B creturn 4:ptr rdRec dc a4'name,var' read variable record name ds 256 shell variable name var ds 257 shell variable value end **************************************************************** * * long labs(long i) * * Return the absolute value of i. * * Inputs: * i - argument * * Outputs: * Returns abs(i). * **************************************************************** * labs start csubroutine (4:i),0 lda i+2 bpl lb1 sub4 #0,i,i lb1 creturn 4:i end **************************************************************** * * long long llabs(long long i) * * Return the absolute value of i. * * Inputs: * i - argument * * Outputs: * Returns abs(i). * **************************************************************** * llabs start imaxabs entry retptr equ 1 csubroutine (8:i),4 stx retptr stz retptr+2 ph8 ~srand if no initialization then bne lb1 ph2 #1 initialize with a value of 1 jsl srand lb1 jsl ~RANX find the random number lda >~SEED and #$7FFF rtl ~srand entry dc i'0' end **************************************************************** * * rsort - recursive sort for qsort * * Inputs: * first - first array element to sort * last - last array element to sort * **************************************************************** * rsort private left equ 1 left address right equ 5 right address csubroutine (4:first,4:last),8 sr0 phb phk plb lda last+2 if last <= first then quit bmi sr1a cmp first+2 bne sr1 lda last cmp first sr1 bgt sr1b sr1a plb creturn sr1b move4 last,right right = last move4 first,left left = first bra sr3 sr2 add4 left,lsize inc left until *left >= *last sr3 plb ph4 ~srand phb plx ply pla phy phx plb brl ~RANX2 end **************************************************************** * * strtof - convert a string to a float * strtold - convert a string to a long double * * Inputs: * str - pointer to the string * ptr - pointer to a pointer; a pointer to the first * char past the number is placed here. If ptr is * nil, no pointer is returned * * Outputs: * X-A - pointer to result * * Note: These are currently implemented by just calling strtod * (in SysFloat). As such, all of these function really * return values in the SANE extended format. * **************************************************************** * strtold start strtof entry jml strtod end **************************************************************** * * strtol - convert a string to a long * * Inputs: * str - pointer to the string * ptr - pointer to a pointer; a pointer to the first * char past the number is placed here. If ptr is * nil, no pointer is returned * base - base of the number * * Outputs: * X-A - converted number * **************************************************************** * strtol start base equ 18 base ptr equ 14 *return pointer str equ 10 string pointer rtl equ 7 return address val equ 3 value negative equ 1 is the number negative? lda #0 pha make room for & initialize val pha pha make room for & initialize negative tsc set up direct page addressing phd tcd ; ; Skip any leading whitespace ; lda ptr if ptr in non-null then ora ptr+2 beq sw1 lda str initialize it to str sta [ptr] ldy #2 lda str+2 sta [ptr],Y sw1 lda [str] skip the white space and #$00FF tax lda >__ctype+1,X and #_space beq cn0 inc4 str bra sw1 ; ; Convert the number ; cn0 lda [str] if the next char is '-' then and #$00FF cmp #'-' bne cn1 inc negative negative := true bra cn2 ++str cn1 cmp #'+' else if the char is '+' then bne cn3 cn2 inc4 str ++str cn3 ph4 errno ldx #$7FFF return value = LONG_MAX ldy #$FFFF lda negative if negative then beq ov1 inx return value = LONG_MIN iny ov1 sty val stx val+2 ; ; return the results ; rt1 pla remove the original value of str from pla the stack lda negative if negative then beq rt2 sub4 #0,val,val val = -val rt2 ldx val+2 get the value ldy val lda rtl fix the stack sta base-1 lda rtl+1 sta base pld tsc clc adc #16 tcs tya return rtl end **************************************************************** * * strtoul - convert a string to an unsigned long * ~strtoul - alt entry point that does not parse leading * white space and sign * * Inputs: * str - pointer to the string * ptr - pointer to a pointer; a pointer to the first * char past the number is placed here. If ptr is * nil, no pointer is returned * base - base of the number * * Outputs: * X-A - converted number * **************************************************************** * strtoul start base equ 22 base ptr equ 18 *return pointer str equ 14 string pointer rtl equ 11 return address rangeOK equ 9 was the number within range? negative equ 7 was there a minus sign? val equ 3 value foundOne equ 1 have we found a number? ldx #0 bra init ~strtoul entry alt entry point called from strtol ldx #1 init pea 1 make room for & initialize rangeOK lda #0 pha make room for & initialize negative pha make room for & initialize val pha pha make room for & initialize foundOne tsc set up direct page addressing phd tcd ; ; Skip any leading whitespace ; txa just process number if called from strtol bne db1c lda ptr if ptr in non-null then ora ptr+2 beq sw1 lda str initialize it to str sta [ptr] ldy #2 lda str+2 sta [ptr],Y sw1 lda [str] skip the white space and #$00FF tax lda >__ctype+1,X and #_space beq db1 inc4 str bra sw1 ; ; Deduce the base ; db1 lda [str] if the next char is '-' then and #$00FF cmp #'-' bne db1a inc negative negative := true bra db1b db1a cmp #'+' skip any leading '+' bne db1c db1b inc4 str db1c lda base if the base is zero then bne db2 lda #10 assume base 10 sta base lda [str] if the first char is 0 then and #$00FF cmp #'0' bne cn1 lda #8 assume base 8 sta base ldy #1 if the second char is 'X' or 'x' then lda [str],Y and #$00DF cmp #'X' bne cn1 asl base base 16 bra db3 db2 cmp #16 if the base is 16 then bne db4 lda [str] if the first two chars are 0x or 0X then and #$DFFF cmp #'X0' bne cn1 db3 add4 str,#2 skip them bra cn1 db4 cmp #37 check for invalid base value bge cn6 dec a beq cn6 ; ; Convert the number ; cn1 lda [str] get a (possible) digit and #$00FF cmp #'0' branch if it is not a digit blt cn5 cmp #'9'+1 branch if it is a numeric digit blt cn2 and #$00DF convert lowercase to uppercase cmp #'A' branch if it is not a digit blt cn5 cmp #'Z'+1 branch if it is not a digit bge cn5 sbc #'A'-11 convert "alpha" digit to value bra cn3 go test the digit cn2 and #$000F convert digit to value cn3 cmp base branch if the digit is too big bge cn5 ldx #1 note that we have found a number stx foundOne pha save the digit pha val = val*base pha pha pha ph4 errno bra rt2a ; ; return the results ; rt1 lda ptr if ptr is non-null then ora ptr+2 beq rt1a lda str set it to str sta [ptr] ldy #2 lda str+2 sta [ptr],Y rt1a lda rangeOK check if number was out of range bne rt2 lda #ERANGE errno = ERANGE sta >errno ldx #$FFFF return value = ULONG_MAX txy bra rt3 rt2 lda negative if negative then beq rt2a sub4 #0,val,val val = -val rt2a ldx val+2 get the value ldy val rt3 lda rtl fix the stack sta base-1 lda rtl+1 sta base pld tsc clc adc #20 tcs tya return rtl end **************************************************************** * * strtoll - convert a string to a long long * * Inputs: * str - pointer to the string * ptr - pointer to a pointer; a pointer to the first * char past the number is placed here. If ptr is * nil, no pointer is returned * base - base of the number * * Outputs: * converted number * **************************************************************** * strtoll start strtoimax entry base equ 26 base ptr equ 22 *return pointer str equ 18 string pointer rtl equ 15 return address retptr equ 11 pointer to location for return value val equ 3 value negative equ 1 is the number negative? lda #0 pha make room for & initialize retptr phx pha make room for & initialize val pha pha pha pha make room for & initialize negative tsc set up direct page addressing phd tcd ; ; Skip any leading whitespace ; lda ptr if ptr in non-null then ora ptr+2 beq sw1 lda str initialize it to str sta [ptr] ldy #2 lda str+2 sta [ptr],Y sw1 lda [str] skip the white space and #$00FF tax lda >__ctype+1,X and #_space beq cn0 inc4 str bra sw1 ; ; Convert the number ; cn0 lda [str] if the next char is '-' then and #$00FF cmp #'-' bne cn1 inc negative negative := true bra cn2 ++str cn1 cmp #'+' else if the char is '+' then bne cn3 cn2 inc4 str ++str cn3 ph4 errno ldx #$7FFF return value = LLONG_MAX ldy #$FFFF lda negative if negative then beq ov1 inx return value = LLONG_MIN iny ov1 sty val sty val+2 sty val+4 stx val+6 ; ; return the results ; rt1 pla remove the original value of str from pla the stack lda negative if negative then beq rt2 negate8 val val = -val rt2 lda val get the value sta [retptr] ldy #2 lda val+2 sta [retptr],y iny iny lda val+4 sta [retptr],y iny iny lda val+6 sta [retptr],y lda rtl fix the stack sta base-1 lda rtl+1 sta base pld tsc clc adc #24 tcs tya return rtl end **************************************************************** * * strtoull - convert a string to an unsigned long long * ~strtoull - alt entry point that does not parse leading * white space and sign * * Inputs: * str - pointer to the string * ptr - pointer to a pointer; a pointer to the first * char past the number is placed here. If ptr is * nil, no pointer is returned * base - base of the number * * Outputs: * converted number * **************************************************************** * strtoull start strtoumax entry base equ 30 base ptr equ 26 *return pointer str equ 22 string pointer rtl equ 19 return address retptr equ 15 pointer to location for return value rangeOK equ 13 was the number within range? negative equ 11 was there a minus sign? val equ 3 value foundOne equ 1 have we found a number? ldy #0 bra init ~strtoull entry alt entry point called from strtoll ldy #1 init lda #0 pha make room for & initialize retptr phx pea 1 make room for & initialize rangeOK pha make room for & initialize negative pha make room for & initialize val pha pha pha pha make room for & initialize foundOne tsc set up direct page addressing phd tcd ; ; Skip any leading whitespace ; tya just process number if called from strtol bne db1c lda ptr if ptr in non-null then ora ptr+2 beq sw1 lda str initialize it to str sta [ptr] ldy #2 lda str+2 sta [ptr],Y sw1 lda [str] skip the white space and #$00FF tax lda >__ctype+1,X and #_space beq db1 inc4 str bra sw1 ; ; Deduce the base ; db1 lda [str] if the next char is '-' then and #$00FF cmp #'-' bne db1a inc negative negative := true bra db1b db1a cmp #'+' skip any leading '+' bne db1c db1b inc4 str db1c lda base if the base is zero then bne db2 lda #10 assume base 10 sta base lda [str] if the first char is 0 then and #$00FF cmp #'0' bne cn1 lda #8 assume base 8 sta base ldy #1 if the second char is 'X' or 'x' then lda [str],Y and #$00DF cmp #'X' bne cn1 asl base base 16 bra db3 db2 cmp #16 if the base is 16 then bne db4 lda [str] if the first two chars are 0x or 0X then and #$DFFF cmp #'X0' bne cn1 db3 add4 str,#2 skip them bra cn1 db4 cmp #37 check for invalid base value jge cn6 dec a jeq cn6 ; ; Convert the number ; cn1 lda [str] get a (possible) digit and #$00FF cmp #'0' branch if it is not a digit blt cn5 cmp #'9'+1 branch if it is a numeric digit blt cn2 and #$00DF convert lowercase to uppercase cmp #'A' branch if it is not a digit blt cn5 cmp #'Z'+1 branch if it is not a digit bge cn5 sbc #'A'-11 convert "alpha" digit to value bra cn3 go test the digit cn2 and #$000F convert digit to value cn3 cmp base branch if the digit is too big bge cn5 ldx #1 note that we have found a number stx foundOne pha save the digit ph8 errno bra rt2a ; ; return the results ; rt1 lda ptr if ptr is non-null then ora ptr+2 beq rt1a lda str set it to str sta [ptr] ldy #2 lda str+2 sta [ptr],Y rt1a lda rangeOK check if number was out of range bne rt2 lda #ERANGE errno = ERANGE sta >errno lda #$FFFF return value = ULLONG_MAX sta [retptr] ldy #2 sta [retptr],y iny iny sta [retptr],y iny iny sta [retptr],y bra rt3 rt2 lda negative if negative then beq rt2a negate8 val val = -val rt2a lda val get the value sta [retptr] ldy #2 lda val+2 sta [retptr],y iny iny lda val+4 sta [retptr],y iny iny lda val+6 sta [retptr],y rt3 lda rtl fix the stack sta base-1 lda rtl+1 sta base pld tsc clc adc #28 tcs tya return rtl end **************************************************************** * * int system(command) * char *command; * * Executes the command steam as an exec file. * * Inputs: * command - command string * * Outputs: * Returns the status of the command * **************************************************************** * system start phb get the addr of the string from the phk stack plb plx ply pla sta exComm pla sta exComm+2 ora exComm sta empty bne lb1 if calling system(NULL) lda #empty use empty command string sta exComm lda #^empty sta exComm+2 lb1 phy execute the command phx Execute ex ldy empty bne ret if doing system(NULL) tya bcs ret error => no command processor inc a (& vice versa) ret plb rtl ex dc i'$8000' exComm ds 4 empty ds 2 end **************************************************************** * * void __record_va_info(va_list ap); * * Record that a traversal of variable arguments has finished. * Data is recorded in the internal va info that will be used * to remove variable arguments at the end of the function. * * Inputs: * ap - the va_list * **************************************************************** * __record_va_info start va_info_ptr equ 1 pointer to the internal va info csubroutine (4:ap),4 ldy #4 get pointer to internal va info lda [ap],y sta va_info_ptr stz va_info_ptr+2 lda [ap] update end of variable arguments cmp [va_info_ptr] blt ret sta [va_info_ptr] ret creturn end **************************************************************** * * void __va_end(internal_va_info *list); * * Remove variable length arguments from the stack. * * Inputs: * list - Pointer to an array. The second element is a * pointer to the first variable argument, while * the first is a pointer to the first byte past * the argument list. * * Notes: * 1. The number of bytes to remove must be even. * 2. D is incremented by the # of bytes removed. * **************************************************************** * __va_end start list equ 7 pointer to the array D equ 1 caller's DP phb save the caller's data bank phd save the caller's D reg tsc set up our stack frame tcd sec calculate the # of bytes to be removed ldy #4 lda [list] sbc [list],Y sta >toRemove clc update the caller's DP adc D sta D lda [list],Y set the source address tax dex lda [list] set the destination address tay dey sec set the # of bytes to move - 1 tsc sbc [list] eor #$FFFF mvp 0,0 move the bytes clc update out stack ptr tsc adc >toRemove tcs pld restore the caller's DP plx remove the parameter from the stack ply pla pla phy phx plb restore the caller's data bank rtl toRemove ds 2 # bytes to remove end