diff --git a/int64.asm b/int64.asm index 450855e..61459a9 100644 --- a/int64.asm +++ b/int64.asm @@ -23,6 +23,7 @@ Int64 start dummy segment * * Outputs: * NUM2 - result +* X - next 16 bits of true result (bits 64-79) * **************************************************************** * @@ -83,6 +84,7 @@ ML2 ROR ANS+14 shift the interim result move4 ANS,NUM2 move return value and address move4 ANS+4,NUM2+4 move4 RETURN-1,NUM1+4 + LDX ANS+8 set X to next 16 bits of result PLD fix stack, DP TSC CLC diff --git a/stdlib.asm b/stdlib.asm index 99d7669..9b260e1 100644 --- a/stdlib.asm +++ b/stdlib.asm @@ -1161,6 +1161,371 @@ rt3 lda rtl fix the stack 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? + + pea 0 make room for & initialize retptr + phx + pea 0 make room for & initialize val + pea 0 + pea 0 + 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 + tdc + clc + adc #val + tax + jsl ~strtoull + lda val+6 + bpl rt1 see if we have an overflow + ldy negative allow LLONG_MIN as legal value + beq ov0 + cmp #$8000 + bne ov0 + lda val + ora val+2 + ora val+4 + beq rt1 +; +; Overflow - flag the error +; +ov0 lda #ERANGE errno = ERANGE + sta >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 + sub8 #0,val,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 pea 0 make room for & initialize retptr + phx + 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 + pea 0 + pea 0 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 val val = val*base + pea 0 + pea 0 + pea 0 + ph2 base + jsl ~UMUL8 + pl8 val + pla get the saved digit + txy branch if there was an error + beq cn3a + stz rangeOK +cn3a clc add in the new digit + adc val + sta val + lda val+2 + adc #0 + sta val+2 + lda val+4 + adc #0 + sta val+4 + lda val+6 + adc #0 + sta val+6 + bcc 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 + 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 + sub8 #0,val,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) diff --git a/stdlib.macros b/stdlib.macros index 6467427..ae5bb32 100644 --- a/stdlib.macros +++ b/stdlib.macros @@ -689,3 +689,27 @@ .f mnote "Missing closing '}'",16 mend + macro +&l sub8 &n1,&n2,&n3 +&l ~setm + ph8 &n1 + ph8 &n2 + jsl ~SUB8 + aif c:&n3,.a + pl8 &n1 + ago .b +.a + pl8 &n3 +.b + ~restm + mend + macro +&l jge &bp +&l blt *+5 + brl &bp + mend + macro +&l jeq &bp +&l bne *+5 + brl &bp + mend