; *************************************************************************** ; CSpread StringToNumeric Routine ; ; Version 1.2 ; ; Written By David Hart (davey3000) 1998-1999 ; *************************************************************************** ; ; Notes: Remember to include the standard TI include files in your main ; program before including this file. ; ; How To Use: Just call CheckStrNumeric to check that a string is a valid ; number, and then call StringToNumeric if it is to convert ; the string to a real number in OP1. Note that this ; DESTROYS THE ORIGINAL STRING. ; ; LEGAL DISCLAIMER ; ; This SOFTWARE (that is, all software programs included with this text file) ; is free-ware, and therefore is provided "as-is" without any kind of warranty. ; I provide no guarantee as to the functionality, usefulness, fitness for a ; particular purpose or merchantability of the SOFTWARE. I may not be held ; liable for any damages whatsoever arising from the use of or inability to ; use the SOFTWARE. ; ; ; Please feel free to use this code however you see fit. I would appreciate ; a small credit in the documentation of any program that you produce ; which uses this code. ; ; ***** Required variables - these can point to any spare place in memory ; decimal_point_pos equ _textShadow+0 ; Position of decimal point ; in a number (2 BYTES) exponent_len equ _textShadow+2 ; Length of the exponent ; string, or zero if none - ; valid when converting a str ; to a number (1 BYTE) ; ***** Check the string pointed to by HL, of length B, to see if it is ; numeric. CF set if non-numeric, otherwise cleared - Destroys A CheckStrNumeric: push bc push de push hl ld c,0 ; C is the status register, and its ; bits mean as follows: ; 0 - Set when decimal point found ; 1 - Set when first non-space char ; found ; 2 - Set when exponent char found ; 3 - Set when first numeric digit ; found ; 4 - Set when exponent char first ; found, reset when next char is ; checked - used to allow a neg ; sign after exponent char ld d,0 ; D holds the index of the current ; digit, relative to the start of the ; number check_byte_numeric: ld a,(hl) ; Get current char cp Lspace ; Spaces are not counted as digits, but jr nz,no_more_spaces ; are accepted if before any other bit 1,c ; chars jr z,is_numeric_byte_no_count jr not_numeric no_more_spaces: set 1,c ; Non-space char found, so no more ; spaces allowed check_exponent: cp Lexponent ; Check for exponent (only one allowed) jr nz,check_dec_point bit 2,c jr nz,not_numeric set 2,c ; Set exponent just found set 4,c res 3,c ; Must be at least one numeric digit jr is_numeric_byte_no_res_exp check_dec_point: cp Lperiod ; Check for decimal point jr nz,check_neg bit 0,c ; Check there is only one decimal jr nz,not_numeric ; point - exit with error if not bit 2,c ; Check no dec. point after exponent jr nz,not_numeric set 0,c jr is_numeric_byte check_neg: cp Lneg ; Check for negative (-) sign jr nz,check_numbers xor a ; Check - sign is at start of number cp d jr z,is_numeric_byte bit 4,c jr nz,is_numeric_byte jr not_numeric check_numbers: cp L0 ; Check for numbers (final filter) jr c,not_numeric cp L9+1 jr nc,not_numeric set 3,c ; Numeric char (digit) found, so record jr is_numeric_byte ; in C that it has been not_numeric: scf ; Non-numeric, so set CF jr check_str_numeric_exit is_numeric_byte: res 4,c ; Clear negative allowance after ; exponent is_numeric_byte_no_res_exp: inc d ; Count digit is_numeric_byte_no_count: inc hl ; Point HL to next char, and loop djnz check_byte_numeric bit 3,c ; Signal non-numeric string if no jr z,not_numeric ; numbers found or a ; Clear CF to signal numeric check_str_numeric_exit: pop hl pop de pop bc ret ; ***** Converts string in HL, length B, to number in OP1 (MUST be checked ; to be numeric with CheckStrNumeric first) - Destroys A AND THE STRING ; IT CHECKS StringToNumeric: push bc push de push hl push hl call _ZEROOP1 ; Clear OP1 pop hl remove_initial_spaces: ld a,(hl) ; Remove all spaces at start of number cp Lspace jr nz,stop_spaces inc hl dec b jr remove_initial_spaces stop_spaces: ld de,_OP1 ; Store pointer to OP1 in DE ld a,(hl) ; Check whether first char of string cp Lneg ; is the -ve sign (-), and set A ld a,0 ; accordingly jr nz,not_negative_number ld a,$80 inc hl dec b not_negative_number: ld (de),a ; Store type of OP1 (real) and sign inc de inc de ; Add 2 to DE to point to mantissa inc de ; ** Remove the exponent and everything after it xor a ; No exponent by default ld (exponent_len),a ld a,Lexponent ; Handle exponent (by altering string call FindByte ; length to remove it, and by setting jr c,no_exponent ; the num_has_exponent variable) ld c,a ; Store exponent value length ld a,b sub c ld (exponent_len),a ld b,c ; Set new string length ld a,c ; If exponent is before any numbers, or a ; then take it as being before 1 jr nz,no_exponent push hl call _op1set1 pop hl jp check_conv_exponent no_exponent: push hl ; Set default decimal point pos ld h,0 ld l,b ld (decimal_point_pos),hl pop hl ; ** Remove the decimal point - makes all future tasks easier ld a,Lperiod ; Find decimal point pos, and store call FindByte jr c,no_dec_point dec b ; Reduce string length to allow for push hl ; removal of the dec. point, and store ld h,0 ; the position of the dec. point ld l,a ld (decimal_point_pos),hl pop hl cp b ; If decimal point is at end of string, jr z,no_dec_point ; it has already been removed fully push bc push de push hl ld h,0 ; Find length of string to move, and ld l,b ; store in BC ld a,(exponent_len) ld e,a ld d,h add hl,de ld de,(decimal_point_pos) or a sbc hl,de ld b,h ld c,l ld de,(decimal_point_pos) pop hl ; Store pointer to dec. point in HL push hl add hl,de ld d,h ; Get rid of decimal point (by setting ld e,l ; HL to point to byte after dec. point, inc hl ; and DE to point to dec. point) ldir pop hl pop de pop bc no_dec_point: find_first_non_zero_digit: ld a,(hl) ; Skip past any superfluous zeros cp L0 jr nz,non_zero_digit_found inc hl ; Check next character is a number - ld a,(hl) ; don't remove zero if not dec hl cp L0 jr nc,another_digit_after cp L9+1 jr c,another_digit_after set_zero_number: call _OP1SET0 ; Main part of number is zero, so just jr no_get_exponent ; set entire number to zero, and exit another_digit_after: ld a,b ; Remove zero digit. Make sure there cp 1 ; is always at least one digit jr z,set_zero_number dec b inc hl push hl ; Correct decimal point pos ld hl,(decimal_point_pos) dec hl ld (decimal_point_pos),hl pop hl jr find_first_non_zero_digit non_zero_digit_found: add_digit_to_op1: ld a,(hl) ; Get first digit as ASCII char inc hl sub L0 ; Convert first char to BCD digit, rlca ; and shift to upper nibble of C rlca rlca rlca ld c,a dec b ; If last digit, then write straight jr nz,is_second_digit ; out to OP1 without a second digit ld (de),a jr fin_conv_number is_second_digit: ld a,(hl) inc hl sub L0 ; Store BCD digit in OP1 or c ld (de),a inc de digit_conv_loop_end: djnz add_digit_to_op1 ; Check all digits fin_conv_number: push hl ld de,(decimal_point_pos) ; Calculate exponent based on decimal ld hl,$FBFF ; point pos add hl,de ld (_OP1EXPM),hl pop hl check_conv_exponent: ld a,(exponent_len) ; Check whether exponent must be or a ; processed jr z,no_get_exponent inc hl ; Adjust HL to point to value after ; exponent dec a ; Decrement exponent length to allow ld b,a ; for removed E sign, and limit ld a,(hl) ; exponent length to 3 digits - allows cp Lneg ; an extra char for the negation (-) ld a,3 ; sign if needed jr nz,no_neg_sign ld a,4 no_neg_sign: call LimitBToA push bc push hl rst 08h ; Store main number in OP2 pop hl pop bc call StringToNumeric ; Convert exponent from string to ; value in OP1 call _CONVOP1 ; Convert exponent from float to binary ; word in DE ld a,(_OP1) or a jr z,normal_adjust_exp ld hl,(_OP2EXPM) ; Adjust exponent of main number or a ; (negative exponent) sbc hl,de ld (_OP2EXPM),hl jr restore_main_number normal_adjust_exp: ld hl,(_OP2EXPM) ; Adjust exponent of main number add hl,de ; (positive exponent) ld (_OP2EXPM),hl restore_main_number: call _OP2TOOP1 ; Restore main number no_get_exponent: pop hl pop de pop bc ret ; ***** Limits the value in B to the value in A (B will be less than or equal ; to A afterwards) LimitBToA: cp b ret nc ld b,a ret ; ***** Finds the first byte of value A in the block of memory pointed to ; by HL of length B - Returns position of value in A, or index of char ; after last char if not found - CF also set if byte not found FindByte: push bc push de push hl ld c,a ; C holds target value ld d,0 ; D holds current byte index check_for_byte: ld a,(hl) ; Search for byte value cp c jr z,byte_found inc d inc hl djnz check_for_byte ld a,d ; Indicate byte not found scf jr exit_find_byte byte_found: ld a,d ; Indicate byte found, and store index or a ; in A exit_find_byte: pop hl pop de pop bc ret