
; --------------------------------------------------------
;
;  Formats a byte size to a simple string representation.
;
;  Type LongLong                    ' 0.44  KB '
;    nFileSizeLow As Long           '  825  MB '
;    nFileSizeHigh As Long          ' 35.7  GB '
;  End Type
;
;  Function FormatBytes(tByteSize As LongLong) As String
;  Function FormatBytes(cByteSize As Currency) As String
;
;  Note - cByteSize should be assigned by API (at bit zero).
;  Assigning to a Currency within VB will adjust the value
;  by multiplying it by 10000.
;
;  api > 34567 > cur == 00000000003.4567
;  cur = 34567 > cur == 00000034567.0000
;
;  Also, assigning unsigned longs to Currency within VB
;  will corrupt large values by sign extending them.
;
; --------------------------------------------------------
;
;  Considering this code is just formatting a single byte
;  size parameter to a small ten character string, it is
;  quite tortuous and relatively large compared to other
;  assembler routines. It has lots of big constants and
;  a generous helping of bog sause.
;
;  However, although it's binary is a massive 456 bytes
;  it is exponentially faster than a VB equivalent algo.
;
; --------------------------------------------------------

 .486            ; 32-bit instruction set
 .model flat     ; Protected memory, 4GB limit
 .code           ; Start of code

FormatBytes proc near

   push ebp
   push esi
   push edi
   push ebx
   mov esi,[esp+24]  ; Pointer to byte size param

   mov edx,8876543h  ; api address patched on init
   push 10           ; SysAllocStringLen len param
   push 0            ; string to copy (null pointer)
   call edx          ; call SysAllocStringLen api
   mov edi, eax      ; store the return pointer

   mov eax,[esi+4]   ; high dword
   test eax, eax
   jnz _high

  _kilo:
   mov ecx, 75       ; 'K'
   mov eax,[esi]     ; low dword
  ;cmp eax, 400h
  ;jb _format        ; < 1 kb

   cmp eax,100000h
   jb _format        ; < 1 mb >= 1 kb

   test eax,0C0000000h
   jnz _giga

  _mega:
   mov eax,[esi+1]
   mov ecx, 77       ; 'M'
   shr eax, 2
   jmp _format       ; < 1 gb >= 1 mb

  _high:
   test eax,0f0000000h
   jnz _exa
   test eax,0ffc0000h
   jnz _peta
   test eax,03ff00h
   jnz _tera

  _giga:
   mov eax,[esi+2]
   mov ecx, 71       ; 'G'
   shr eax, 4
   jmp _format       ; < 1 tb >= 1 gb

  _tera:
   mov eax,[esi+3]
   mov ecx, 84       ; 'T'
   shr eax, 6
   jmp _format       ; < 1 pb >= 1 tb

  _peta:
   mov eax,[esi+5]
   mov ecx, 80       ; 'P'
   and eax,0fffffh
   jmp _format       ; < 1 eb >= 1 pb

  _exa:
   movzx eax, word ptr [esi+6]
   mov ecx, 69       ; 'E'
   shr eax, 2        ; = 1 to 15 eb

  _format:                   ; ' 3.57  GB '
   mov ebx,200020h           ; ' 1023',' 98.7','  325'
   mov [edi], ebx            ; '  '
   mov [edi+4],ebx           ; '    '
   mov [edi+8],ebx           ; '      '
   mov [edi+12],bx           ; '       '
   mov [edi+14],cx           ; 'K','M','G','T','P','E'
   mov [edi+16],word ptr 42h ; 'B'
   mov [edi+18],bx           ; ' '

   mov ebx, eax
   shr eax, 10
   jz _0whole

   mov edx, 3030h
   mov ecx, edx

   cmp ax, 1000
   jb _1
   sub ax, 1000   ; only slightly faster than div
   inc ch         ; but cheaper on the registers
   jmp _2
  _1:
   cmp ax, 100
   jb _2
   sub ax, 100
   inc cl
   jmp _1
  _2:
   cmp al, 10
   jb _write
   sub al, 10
   inc ah
   jmp _2

  _0whole:
   mov eax, 30h   ; short circuit zero 0.xx
   jmp _1whole

  _write:         ; 987  -> ch=0, cl=9, ah=8, al=7
   or eax, edx    ; 1023 -> ch=1, cl=0, ah=2, al=3

   cmp ecx, edx
   je _2whole
   cmp cl, dl
   je _4whole

  ;3whole:
   mov [edi+4], cl
   mov [edi+6], ah
   mov [edi+8], al
   jmp _return

  _4whole:    ; 1000 to 1023
   mov [edi+2], ch
   mov [edi+4], cl
   mov [edi+6], ah
   mov [edi+8], al
   jmp _return

  _2whole:
   cmp ah, dh
   je _1whole
   mov [edi+2], ah           ; ' 21.x  EB '
   mov [edi+4], al
   mov [edi+6], byte ptr 2eh ; '.'
   mov ebp, 8
   jmp _deci

  _1whole:
   mov [edi+2], al           ; ' 3.xx  GB '
   mov [edi+4], byte ptr 2eh ; '.'
   mov ebp, 6

  _deci:  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   and ebx, 3ffh
   mov ecx, 3030h
   jz _last       ; short circuit zero remaider

;   mov edx, 1000  ; normlize 1024 to 1000 (%)
;   mul edx        ; e.g 1023b = 0.999 KB
;   shr eax, 10    ; inaccurate and slower

   mov eax, ebx
   shl ebx, 3
   mov edx, ebx   ; normlize 1024 to 1000 (%)
   shl ebx, 1     ; e.g 1023b = 0.999 KB
   add ebx, edx
   shr ebx, 10
   sub eax, ebx

   cmp eax, 10
   jl _min

   cmp eax, 1000
   jb _3
   mov ecx, 3939h
   jmp _last

  _3:
   cmp ax, 100
   jb _4
   sub ax, 100
   inc ch
   jmp _3
  _4:
   cmp ax, 10
   jb _last
   sub ax, 10
   inc cl
   jmp _4

  _min:
   inc ecx        ; small non-zero rounds to .01

  _last:
   mov [edi+ebp],ch
   cmp ebp, 8
   je _return
   mov [edi+8], cl

  _return:
   mov ebx,[esp+28]
   mov [ebx],edi  ; ' 3.57  GB '

  _exitp:
   pop ebx
   pop edi        ; restore registers
   pop esi
   pop ebp
   xor eax, eax   ; clear eax tell VB all okay
   ret 12         ; cleanup (lpObj, p1, rc)

FormatBytes endp  ; end of procedure
end FormatBytes   ; end of code, indicate entry point
