Login

Subversion Repositories NedoOS

Rev

Blame | Last modification | View Log | Download | RSS feed

#ifndef included_formatstr
#define included_formatstr

; This routine for taking a base-10 exponent and a string of digits and (without
; a decimal) and inserting a decimal, any leading zeros, stripping trailing
; zeros, and appending an exponent if needed.



; Define the absolute maximum number of digits in the result string.
; If FORMAT_LEN is bigger than this value, then it will be reduced.
#ifndef MAX_FORMAT_LEN
#define MAX_FORMAT_LEN 19
#endif

; Define this to use an external reference to get the number of digits used
;#define EXTERNAL_FORMAT_LEN <pointer>
;
; For example, TI-OS has a byte at fmtDigits:
;#define EXTERNAL_FORMAT_LEN fmtDigits

; This is the max number of digits in the output.
; If EXTERNAL_FORMAT_LEN is used and it is 0, then this will be used.
#ifndef FORMAT_LEN
#define FORMAT_LEN 18
#endif

; Define this to use an external reference for the max exponent before switching
; to enginieering mode.
;#define EXTERNAL_FORMAT_MAX_ENGINEERING  <pointer>

; Define the max exponent to use before switching to engineering format
; If none is defined, this uses FORMAT_LEN.
; If EXTERNAL_FORMAT_MAX_ENGINEERING is used and is 0 or larger than FORMAT_LEN,
; then this will be used.
;#define FORMAT_MAX_ENGINEERING


; Define this to use an external reference for the min exponent before switching
; to enginieering mode.
;#define EXTERNAL_FORMAT_MIN_ENGINEERING  <pointer>

; Define the largest negative exponent to use engineering format
; If EXTERNAL_FORMAT_MIN_ENGINEERING is used and is 0 or larger than FORMAT_LEN,
; then this will be used.
#ifndef FORMAT_MIN_ENGINEERING
#define FORMAT_MIN_ENGINEERING  -5  ; causes exponent of -5 to be enginierring
#endif

; For formatting, we need to define these three characters
; "Enginieering e" for values like "1.23456e10"
#ifndef TOK_ENG
#define TOK_ENG 'e'
#endif

; Negative sign
#ifndef TOK_NEG
#define TOK_NEG '-'
#endif

; Decimal point
#ifndef TOK_DECIMAL
#define TOK_DECIMAL '.'
#endif

formatstr:
;Inputs:
;   HL points to the null-terminated string of digits
;   DE is the signed exponent.
;Outputs:
;   The string has leading and trailing zeros stripped, a decimal is placed
;   (if needed), and an exponent field is appended (if needed).
;Destroys:
;   HL, DE, BC, AF
;Notes:
;   This routine operates in-place. It assumes that there is enough space
;   allocated for the string. At most MAX_FORMAT_LEN+10 bytes is needed,
;   assuming the exponent can be up to 5 digits long.
;
; Skip over the negative sign, if any
  ld a,(hl)
  cp TOK_NEG
  jr nz,$+3
  inc hl
  push de ; save the exponent
  push hl

;Strip leading zeros
  ld d,h
  ld e,l
  ld a,'0'
  cp (hl)                           ;These two lines can be commented-out to save three
  jr nz,formatstr_no_leading_zeros  ;bytes at the expense of redundant processing.
formatstr_strip_leading_zeros:
  cpi
  jr z,formatstr_strip_leading_zeros
  dec hl

; HL points to the first non-'0' digit
; DE points to the first digit
; Copy bytes from HL to DE until 0x00 is reached at HL or FORMAT_LEN digits are copied
#ifdef EXTERNAL_FORMAT_LEN
  ld a,(EXTERNAL_FORMAT_LEN)
  or a
  jr nz,$+4
#endif
  ld a,FORMAT_LEN
#ifdef EXTERNAL_FORMAT_LEN
  cp MAX_FORMAT_LEN
  jr c,$+4
  ld a,MAX_FORMAT_LEN
#endif
  ld c,a

  xor a
  ld b,a
formatstr_copy_digits:
  cp (hl)
  ldi
  jp po,+_
  jr nz,formatstr_copy_digits
_:
  dec hl
  ld (hl),0

formatstr_no_leading_zeros:
; there are no more leading zeros
; Truncate the number of digits if necessary based on FORMAT_LEN

  call formatstr_remove_trailing_zeros
  pop hl  ; points to the first digit
  pop de  ; exponent

; Make sure the first digit isn't 0x00
  ld a,(hl)
  or a
  jr nz,+_
  ld (hl),'0'
  inc hl
  ld (hl),a
  ret
_:

  call formatstr_check_eng
  jr c,formatstr_eng
  inc de
  bit 7,d
  jr nz,formatstr_neg_exp
  ; Otherwise, we need to insert a decimal after DE digits (D is 0, though)
  ld b,d
  ld c,e
  xor a
  cp e
  jr z,formatstr_insert_decimal
  cpir
  ; If we have reached a 0x00, we may need to pad with zeros
  jr z,formatstr_pad_right
  ; otherwise, let's insert the decimal
formatstr_insert_decimal:
  ld a,(hl)
  or a
  ret z
  ld a,TOK_DECIMAL
formatstr_insert_decimal_loop:
  ld c,(hl) ; back up digit
  ld (hl),a
  inc hl
  ld a,c
  or a
  jr nz,formatstr_insert_decimal_loop
  ld (hl),a
  ret

formatstr_neg_exp:
; Need to pad -DE 0s to the left
  xor a
  ld c,a
  cpir
  ld b,a
  sub c
  ld c,a
  ;HL-1 is where to start reading bytes
  ;HL-DE is where to start writing bytes
  xor a
  ld d,a
  sub e     ; A is the number of 0s to write
  ld e,a
  ex de,hl
  add hl,de
  ex de,hl
  dec hl
  ; DE points to where to write the last byte
  ; HL points to where to read it from
  ; BC is the number of bytes to copy (it will be non-zero)
  ; A is the number of zeros to insert
  lddr
  ;now from DE backwards, write A '0's
  ex de,hl
  ld b,a
  ld (hl),'0'
  dec hl
  djnz $-3
  ; finally, write a '.'
  ld (hl),TOK_DECIMAL
  ret

formatstr_pad_right:
  ; append BC+1 0s
  dec hl
  inc bc
_:
  ld (hl),'0'
  cpi
  jp pe,-_
  ld (hl),0
  ret

formatstr_eng:
; need to insert a decimal after the first digit
  inc hl
  call formatstr_insert_decimal
  ld (hl),TOK_ENG
  inc hl
  bit 7,d
  jr z,formatstr_exp_to_str
  ld (hl),TOK_NEG
  inc hl
  xor a
  sub e
  ld e,a
  sbc a,a
  sub d
  ld d,a
formatstr_exp_to_str:
  ex de,hl
  ld a,'0'-1
  ld bc,-10000
_:
  inc a
  add hl,bc
  jr c,-_
  cp '0'
  jr z,$+4
  ld (de),a
  inc de

  ld a,'9'+1
  ld bc,1000
_:
  dec a
  add hl,bc
  jr nc,-_
  cp '0'
  jr z,$+4
  ld (de),a
  inc de

  ld a,'0'-1
  ld bc,-100
_:
  inc a
  add hl,bc
  jr c,-_
  cp '0'
  jr z,$+4
  ld (de),a
  inc de

  ld b,10
  ld a,l
_:
  add a,10
  dec b
  jr nc,-_
  ex de,hl
  jr z,formatstr_eng_last_digit
  set 4,b
  set 5,b
  ld (hl),b
  inc hl
formatstr_eng_last_digit:
  add a,'0'
  ld (hl),a
  inc hl
  ld (hl),0
  ret

formatstr_remove_trailing_zeros:
  ; first, seek the end of the string
  xor a
  ld c,a
  ld b,a
  cpir
  dec hl
  ld a,'0'
_:
  dec hl
  cp (hl)
  jr z,-_
  inc hl
  ld (hl),0
  ret

formatstr_check_eng:
; Return carry flag set if engineering format is required, else nc
;
; If the exponent is greater than FORMAT_MAX_ENGINEERING, then use enginieering
; notation. Note that FORMAT_MAX_ENGINEERING < 256, so check that D = -1 or 0
  ld a,d
  inc a
  jr z,formatstr_check_eng_neg
  add a,254
  ret c   ;the exponent is too big in magnitude, engineering format is required.
; The exponent is positive and less than 256

#ifdef EXTERNAL_FORMAT_MAX_ENGINEERING
  ld a,(EXTERNAL_FORMAT_MAX_ENGINEERING)
  or a
  jr nz,+_
#ifdef FORMAT_MAX_ENGINEERING
  ld a,FORMAT_MAX_ENGINEERING
#else
#ifdef EXTERNAL_FORMAT_LEN
  ld a,(EXTERNAL_FORMAT_LEN)
  or a
  jr nz,$+4
#endif
  ld a,FORMAT_LEN
#endif

_:
#else
#ifdef FORMAT_MAX_ENGINEERING
  ld a,FORMAT_MAX_ENGINEERING
#else
#ifdef EXTERNAL_FORMAT_LEN
  ld a,(EXTERNAL_FORMAT_LEN)
  or a
  jr nz,$+4
#endif
  ld a,FORMAT_LEN
#endif
#endif
  cp e
  ret

formatstr_check_eng_neg:
;The exponent is negative and greater than or equal to -256

#ifdef EXTERNAL_FORMAT_MIN_ENGINEERING
  ld a,(EXTERNAL_FORMAT_MIN_ENGINEERING)
  or a
  jr nz,$+3
  ld a,FORMAT_MIN_ENGINEERING
#else
  ld a,FORMAT_MIN_ENGINEERING
#endif
  cp e
  ccf
  ret
#endif