keep obj/stdlib mcopy stdlib.macros case on **************************************************************** * * StdDef - Standard Definitions * * This code implements the tables and subroutines needed to * support the standard C library STDDEF. * * December 1988 * Mike Westerfield * * Copyright 1988 * Byte Works, Inc. * * Note: Portions of this library appear in SysFloat * **************************************************************** * StdDef start dummy segment copy equates.asm end **************************************************************** * * void abort() * * Stop the program. * **************************************************************** * abort start ph2 #SIGABRT jsl raise lda #-1 jmp ~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 **************************************************************** * * 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 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 compare the array elements ph4 key jsl jsl jsl tax quit if *addr = *key beq lb6 bmi lb2 if *key > *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 jmp ~C_QUIT end quick_exit start jsr ~QUICKEXIT lda 4,S jmp ~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 **************************************************************** * * ldiv_t ldiv(n,d) * long n,d; * * Inputs: * n - numerator * d - denominator * * Outputs: * ldiv_t - contains result & remainder * **************************************************************** * ldiv start addr equ 1 csubroutine (4:n,4:d),4 phb use local addressing phk plb ph4 n do the divide ph4 d jsl ~DIV4 pl4 div_t pl4 div_t+4 lda n+2 if the numerator is negative then bpl lb1 sub4 #0,div_t+4,div_t+4 make the remainder negative lb1 lla addr,div_t return the result plb creturn 4:addr div_t ds 8 end **************************************************************** * * lldiv_t lldiv(long long n, long long d) * * Inputs: * n - numerator * d - denominator * * Outputs: * lldiv_t - contains result & remainder * **************************************************************** * lldiv start imaxdiv entry addr equ 1 csubroutine (8:n,8:d),4 phb use local addressing phk plb ph8 n do the divide ph8 d jsl ~CDIV8 pl8 lldiv_t pl8 lldiv_t+8 lla addr,lldiv_t return the result plb creturn 4:addr lldiv_t ds 16 end **************************************************************** * * void qsort(base, count, size, compar) * void *base; * size_t count, size; * int (*compar)(const void *, const void *) * * Inputs: * base - start address of the array to sort * count - # elements in the array * size - size of each array element * compar - function that compares array elements * * Outputs: * The array is sorted on exit. * **************************************************************** * qsort start csubroutine (4:base,4:count,4:size,4:compar),0 phb phk plb dec4 count set count to the addr of the last entry mul4 count,size add4 count,base move4 size,lsize save size in a global var lda compar set the jsl addresses sta jsl1+1 sta jsl2+1 lda compar+1 sta jsl1+2 sta jsl2+2 ph4 count do the sort ph4 base jsl rsort plb creturn end **************************************************************** * * rand - get a random number * * Outputs: * A - random number * **************************************************************** * rand start lda >~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 phb phk plb sr0 lda last+2 if last <= first then quit cmp first+2 bne sr1 lda last cmp first sr1 bgt sr1a plb creturn sr1a move4 last,right right = last move4 first,left left = first bra sr3 sr2 add4 left,lsize inc left until *left >= *last sr3 ph4 last ph4 left jsl1 entry jsl jsl1 tax bmi sr2 sr4 lda right quit if right = first cmp first bne sr4a lda right+2 cmp first+2 beq sr4b sr4a sub4 right,lsize dec right until *right <= *last ph4 last ph4 right jsl2 entry jsl jsl2 dec A bpl sr4 sr4b ph4 left swap left/right entries ph4 right jsr swap lda left+2 loop if left < right cmp right+2 bne sr5 lda left cmp right sr5 blt sr2 ph4 right swap left/right entries ph4 left jsr swap ph4 left swap left/last entries ph4 last jsr swap sub4 left,lsize,right sort left part of array ph4 right ph4 first jsl rsort add4 left,lsize,first sort right part of array brl sr0 ; ; swap - swap two entries ; l equ 3 left entry r equ 7 right entry swap tsc set up addressing phd tcd ldx lsize+2 move 64K chunks beq sw2 ldy #0 sw1 lda [l],Y tax lda [r],Y sta [l],Y txa sta [r],Y dey dey bne sw1 inc l+2 inc r+2 dex bne sw1 sw2 lda lsize if there are an odd number of bytes then lsr A bcc sw3 short M move one byte lda [l] tax lda [r] sta [l] txa sta [r] long M inc4 l inc4 r lda lsize lsr A sw3 asl A quit if there are no more bytes beq sw6 tay bra sw5 sw4 lda [l],Y move the bytes tax lda [r],Y sta [l],Y txa sta [r],Y sw5 dey dey bne sw4 lda [l] tax lda [r] sta [l] txa sta [r] sw6 pld plx tsc clc adc #8 tcs phx rts ; ; local data ; lsize entry ds 4 local copy of size end **************************************************************** * * srand - seed the random number generator * * Inputs: * 4,S - random number seed * **************************************************************** * srand start lda #1 sta >~srand phb plx ply pla phy phx plb brl ~RANX2 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? pea 0 make room for & initialize val pea 0 pea 0 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 str save the starting string ph2 base convert the unsigned number ph4 ptr ph4 str jsl ~strtoul stx val+2 sta val txy see if we have an overflow bpl rt1 ldy negative allow -2147483648 as legal value beq ov0 cpx #$8000 bne ov0 tay beq rt1 ; ; Overflow - flag the error ; ov0 lda #ERANGE errno = ERANGE sta >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 pea 0 make room for & initialize negative pea 0 make room for & initialize val pea 0 pea 0 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 val pea 0 ph2 base _LongMul pl4 val pla branch if there was an error ora 1,S plx ply tax beq cn3a stz rangeOK cn3a clc add in the new digit tya adc val sta val bcc cn4 inc val+2 bne cn4 stz rangeOK cn4 inc4 str next char bra cn1 cn5 lda foundOne if no digits were found, flag the error bne rt1 cn6 lda #EINVAL sta >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 **************************************************************** * * 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 plb Execute ex ldy empty bne ret if doing system(NULL) tya bcs ret error => no command processor inc a (& vice versa) ret rtl ex dc i'$8000' exComm ds 4 empty ds 2 end **************************************************************** * * void __va_end(list) * va_list 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