Shop OBEX P1 Docs P2 Docs Learn Events
PASM Coding help: Integer to ASCII - ASCII to Integer — Parallax Forums

PASM Coding help: Integer to ASCII - ASCII to Integer

Mike GMike G Posts: 2,702
edited 2011-07-04 08:22 in Propeller 1
Can some one point me to a resource for converting an integer to ASCII and an ASCII string to an integer using PASM? I knocked this conversion out in SPIN without much effort. For some reason converting SPIN to PASM has got me reeling. I think my base 10 SPIN approach is clogging up my thought process.

Comments

  • kuronekokuroneko Posts: 3,623
    edited 2011-02-26 21:54
    Picking one from the Similar Threads display [thread=115405]Technique(s) for converting integer to ascii bit pattern in pasm[/thread].
  • bill190bill190 Posts: 769
    edited 2011-02-27 09:06
    Also of help, here is an ASCII chart which shows values for Dec, Hex, and Oct (Decimal, Hexadecimal, and Octal)...

    http://www.asciitable.com/index/asciifull.gif

    And conversion to binary,,,
    http://www.ascii.cl/conversion.htm
  • SRLMSRLM Posts: 5,045
    edited 2011-02-27 10:00
    dec_to_ASCII            'Converts an unsigned number in a register to an ASCII value
                            't1 - The decimal number
                            't2 - The current count of the digits (ie, how many 100's are in 843)
                            't3 - The current number of digit (1000(4), 10(2), 1(1))
                            't4 - (parameter) the number to convert
                            't5 - (parameter) the number of digits in the number (modified)
                            '       ---too few will result in tuncation of upper digits
                            'ASCII_num - (result) will contain the number, optional negative (-, counts as digit)
    
                            movs    :main_loop, #decimal
                            movd    :write, #ASCII_num
                            mov     t3, #10
                            
                            
    :main_loop              mov     t1, 0-0                 'Copy the decimal number (1, 1000, 10000, etc) into the variable
                            mov     t2, #0                  'Clear the counter
            
    :multiply               cmp     t4, t1          wc      'Is the number less than the decimal number?
                  if_nc     sub     t4, t1                  'The decimal number is <= than the value
                  if_nc     add     t2, #1                  'increment the digit count
                  if_nc     jmp     #:multiply
    
                            add     t2, #48                 'Increase to ASCII 0
    
    :write                  mov     0-0, t2
                            
                            cmp     t5, t3          wc      'Should I write move on to the next digit?
                  if_nc     add     :write, destination_mask'Move on to the next digit in ASCII_num
                            add     :main_loop, #1          'Move on to the next digit in decimal
                            djnz    t3, #:main_loop
                             
    
    dec_to_ASCII_ret        ret
    
    ASCII_to_dec            'Routine to convert a string of ASCII numbers to decimal
                            'Takes two parameters:
                            '       a) the string (in long array ASCII_num)
                            '       b) the length (in long ASCII_num_length, 1<=length<=9)
                            'The array is stored MSD first. For example:
                            'The number 756 is stored like:
                            'ASCII_num              7
                            '                       5
                            '                       6
                            'Supports up to 9 digits
                            'Algorithm: convert from number from right to left (6, 5, 7)
                            '
                            'Returns 0 if there isn't a number (aka, ASCII_num_length == 0)
                            '
                            'Result is in t5
                            
                            't6 is the pointer to the current ASCII digit
                            't7 is the current ASCII digit value (ASCII 6 - #48, for example)
                            
                            mov     t5, #0                        'clear the result register
                            mov     t6, #ASCII_num
                            add     t6, ASCII_num_length
                            movs    :loop, t6                     'Set the loop mov to the current ASCII digit
                            sub     :loop, #1                     'First number is at position n-1
                            cmp     ASCII_num_length, #0    wz    'test to make sure that there is actually a number
            if_z            mov     t5, #0                        'if there isn't a number, return 0
            if_z            jmp     :done
                            
    :loop                   mov     t7, 0-0                       'Copy the ASCII number (0-0 will point to ASCII_num) 
                            sub     t7, #48         wz            'Subtract an ASCII '0' (zero) from the current digit
                            sub     :loop, #1                     'Move pointer down for next time
            if_z            add     :mult_loop, #1                'increment to the next decimal number if it's a zero 
            if_z            djnz    ASCII_num_length, #:loop      'if the number is zero, skip this digit and get the next
            if_z            jmp     #:done                        'if the number is zero, and it's the last one go to done     
    
    :mult_loop              add     t5, decimal                   'Add the decimal (10, 1_000, etc.) to the result (will be self-modified in source)
                            djnz    t7, #:mult_loop                'Decrement one from the ASCII placeholder, keep looping if more
    
                            add     :mult_loop, #1                'move to next decimal up
                            djnz    ASCII_num_length, #:loop
    
    :done                   movs    :mult_loop, #decimal
    ASCII_to_dec_ret        ret 
    

    From two different objects that I wrote. May not be symmetric. Also not really tuned for efficiency (memory or speed), although it should be reasonable for most cases.
  • Mike GMike G Posts: 2,702
    edited 2011-02-27 15:37
    Thanks all...

    This is what I came up with. It looks a lengthy compared to SRLM's version. This exercise was much more involved than I thought; self-modifying code, division, looping...

    If Johnny Mac is out there... Integer to ASCII could make a good Spin Zone article?
    DAT
                            org     0
    entry
                            mov     t1, par                 ' points to command
                            mov     cmdptr, t1              ' save pointer to command
                            add     t1, #4
                            mov     rsltptr, t1             ' save result pointer
      
    getcmd                  rdlong  cmd, cmdptr     wz      ' Wait until the command is not zero
                            '***debug result *****
                            rdlong  rslt, rsltptr           ' testing
                            '***debug result ***** 
            if_z            jmp     #getcmd
    
    switch_cmd              cmp     cmd, #1         wz
            if_e            jmp     #cmdset
                            cmp     cmd, #2         wz
            if_e            jmp     #toascii
                            cmp     cmd, #3         wz
            if_e            jmp     #match         
                            jmp     #getcmd                 ' default jump
    
    cmdset                  jmp     #getcmd                 ' Reset the command
    '-------------------------------------------------------------------------
    ' Number to ASCII
    '-------------------------------------------------------------------------
    toascii                 add     t1, #4                  ' next hub long
                            rdlong  srcptr, t1              ' source string pointer
                            mov     i, #0                   ' init counter
                            rdlong  srcobj, srcptr          ' grab the number to convert
    
                            add     t1, #4                  ' next hub long 
                            rdlong  dstptr, t1              ' destination string pointer
    
    :_main                  movd    :buffer, #workspace     ' initialize destination (buffer) address
                            mov     x, srcobj               ' get source number to convert
                            
    :loop                   mov     y, #10                  ' set divisor = 10
                            call    #divide                 ' divide routine
                            mov     t1, x                   ' save remainder[31..16] and quotient[15..0]
                            shr     t1, #16                 ' shift the remainder to [15..0]
                            shl     x, #16                  ' get quotient by shifting out the remainder
                            shr     x, #16                  ' shift back to [15..0]
                         
                            add     t1, #$30                ' convert the remainder to an ascii char
    :buffer                 mov     0-0, t1                 ' stick the char in the cog buffer
                            add     :buffer, incr           ' increment the buffer pointer
                            add     i, #1                   ' count the items in the buffer
    
                            cmp     x, #0           wz      ' are we done?
            if_ne           jmp     #:loop                  ' no = loop; yes = finish conversion
            
                            mov     t1, #workspace          ' set up the cog buffer pointer
                            add     t1, i                   ' go to the end of the buffer
                            sub     t1, #1                  ' note: the chars are in reversed in the buffer
    
    write_to_hub              
                            movs    :readbuff, t1           ' set the source pointer
    :loop                   sub     i, #1                   ' decr buffer item counter
    :readbuff               mov     t1, 0-0                 ' get a byte from the buffer
                            wrbyte  t1, dstptr              ' write the byte to hub memory
                            add     dstptr, #1              ' increment hub memory pointer
                            sub     :readbuff, #1           ' decrement cog pointer
                            cmp     i, #0 wz                ' are we done?
            if_ne           jmp     #:loop                  ' false = get next; true = finish up
            
    :done                   mov     dstobj, #0              ' get terminating zero (0)
                            wrbyte  dstobj, dstptr          ' write 0 to hub memory 
    
    :rtn                    wrbyte  i, rsltptr              ' write result to hub
                            wrlong  zero, cmdptr            ' clear command                           
                            jmp     #getcmd                 ' reset                     
    
    '--- Functions -----
    ' Divide x[31..0] by y[15..0] (y[16] must be 0)
    ' on exit, quotient is in x[15..0] and remainder is in x[31..16]
    ' This code is from the Propeller Manual Appendix B
    divide                  shl     y,#15                   'get divisor into y[30..15]
                            mov     t,#16                   'ready for 16 quotient bits
    :loop                   cmpsub  x,y              wc     'y =< x? Subtract it, quotient bit in c
                            rcl     x,#1                    'rotate c into quotient, shift dividend
                            djnz    t,#:loop                'loop until done
    divide_ret              ret                             'quotient in x[15..0],
                                                            'remainder in x[31..16]
    
    '                        
    ' Variables
    '
    maxcnt                  long    $800
    incr                    long    1<<9
    '---------
    t1                      long    0
    t2                      long    0
    t3                      long    0
    t4                      long    0
    zero                    long    0
    '---------
    i                       long    0
    j                       long    0
    '---------
    cmdptr                  long    0
    srcptr                  long    0
    patptr                  long    0
    dstptr                  long    0
    '---------  
    cmd                     long    0
    srcobj                  long    0
    patchr                  long    0
    dstobj                  long    0
    '----------
    rslt                    long    0
    rsltptr                 long    0
    '----------
    offst                   long    0
    binry                   long    0
    '----------
    x                       long    0
    y                       long    0
    t                       long    0
    '----------
    workspace               res     16
    
                            fit
    

    Next up, ASCII to integer. Should be a little easier, I hope.
  • ChrisGaddChrisGadd Posts: 310
    edited 2011-03-02 13:54
    If I understand the problem right, you wanna convert a hex value into an ascii-encoded decimal value? I had a similar problem a while ago and found a really neat algorithm: Here.
    Essentially, it works by shifting the hex value left into a destination register one bit at a time, and adding 3 to any nibble of the destination that is greater than 4.
    {{                        BCD       Hex
            Start          0000_0000|0111_1011 = $7B = 123
       1    Shift left             0|1111_011
       2    Shift left            01|1110_11
       3    Shift left           011|1101_1
       4    Shift left          0111|1011
            Add 3               1010|1011
       5    Shift left        1_0101|011
            Add 3             1_1000|011
       6    Shift left       11_0000|11
       7    Shift left      110_0001|1
            Add 3          1001_0001|1
       8    Shift left   1 0010_0011|           = $0123
    }}
    
    Hex_to_BCD
                            mov       BCD_Value,#00
                            mov       BitCtr,#32                             ' Count 32 bits to shift
    :Outer_Loop
                            mov       NibCtr,#8                              ' Rotate eight nibbles in BCD_Value
    :Nibble_Loop      
                            rol       BCD_Value,#4                           ' Rotate high nibble into low nibble location
                            mov       Temp,BCD_Value                         ' Copy rotated value    
                            and       Temp,#$0F                              ' Mask the low nibble
                            cmp       Temp,#05                wc             ' Is masked value greater than 4?
              if_nc         add       BCD_Value,#03                          ' Add three if nibble is greater than 4
                            djnz      NibCtr,#:Nibble_Loop                   ' Loop until all eight nibbles are checked
                            shl       Hex_Value,#1            wc             ' Shift msb out of Hex_Value into C
                            rcl       BCD_Value,#1                           ' Rotate C into BCD_Value
                            djnz      BitCtr,#:Outer_Loop                    ' Loop until all 32 bits of long are shifted
    
    This converts the hex value into BCD, packed one digit per nibble. From here you'd just take the nibbles one at a time, add an ASCII offset of $30, and send to the LCD. The above routine can handle a decimal value of up to 99,999,999, any more than that and you'll need to modify it to use two longs for the destination.

    The algorithm works just as well in reverse to convert decimal back into hex, shifting the BCD value right one bit at a time into the destination, still checking all nibbles of the BCD value and subtracting 3 from any nibble that is greater than 4.
  • rymanryman Posts: 12
    edited 2011-04-16 16:16
    Nice. How do you convert string to numbers and numbers to strings in SPIN?
  • Cluso99Cluso99 Posts: 18,069
    edited 2011-04-17 04:01
    For converting numbers (32 bit longs) to numbers look at the spin code in the FullDuplexSerial object that comes with PropTool (or in the obex). There are routines for long to hex, decimal, binary. Kye has also produced some reverse conversions which I think are in the obex under his serial objects. Kye's code has excellent comments.
  • tonyp12tonyp12 Posts: 1,951
    edited 2011-04-17 14:10
    I think it's great puzzle, that also have real uses.

    So you have a 32bit data in memory, of course that is binary data.
    And that we can also see it as dec and hex is just alternative version that
    'data viewer' / assembler shows it.

    Converting this binary to a 8 digit ascii hex would be great start.
    unfortunately ascii A-F does not follow directly after 0-9
    So either create a look up table, or with some controlled additions.
    non tested code:
            mov buffer, data        'data have the original 32bit value.
            movd label, #myhex
            mov counter, #8
    loop
            mov buffer2, buffer
            and buffer2, #%1111     'just get the lower 4 bits a 0-F value.
            add buffer2, #48        'bring it up to ascii code for 0
            cmp buffer2, #58 wc     'was it higher that 9?
       if_c add buffer2, #7         'make it A-F
    label   mov 0-0, buffer2 
            add label, _my9
            ror buffer,#4           'shift nibble
    
            dnjz counter, #loop
    
    _my9    long 1<9
    myhex   res 8
    buffer  res 1
    buffer2 res 1
    counter res 1
    

    This should set 8 longs to ascii hex,
    if you want reversed order use #myhex+8 and use sub label, _my9
    if you want 8 bytes in 2 longs when it would need more coding.
  • at_jayat_jay Posts: 16
    edited 2011-07-04 05:36
    Hi Mike,
    can you please email me the spin code of integer to ASCII conversion for propeller?
    my email is at_jay@yahoo.com.

    Thanks
    Jay
  • Mike GreenMike Green Posts: 23,101
    edited 2011-07-04 08:22
    There are already Spin routines posted for integer to ASCII and ASCII to integer in the Object Exchange. FullDuplexSerialPlus has the ASCII to integer and pretty much any display object (or FullDuplexSerial) has integer to ASCII.


    I couldn't find FullDuplexSerialPlus in the Object Exchange. Maybe it's part of some other package (like the Propeller Education Kit software). In any event, I've attached my copy of it.
Sign In or Register to comment.