We will now apply logical masking and a shift instruction to the HEXNUM program (Figure 4-3). In this new version, shown in Figure 6-1, we will encode the input digits as ASCII characters and store them in memory, using a null byte to terminate the string. This program illustrates the instruction types just introduced while presenting programming techniques that will continue in future examples. Character strings are typically stored in memory with a NUL character (Table 2-3) marking their end. This allows the processing of strings of arbitrary length by first testing each byte against zero (NUL). Thus the loop in HEXNUM2 is of the form while <nonzero> do…, requiring a predicated exit branch at the top and an unconditional return branch at the bottom. The central portion of this program has four tasks: to load each character code as a zero-extended 64-bit value in register r20; to ensure that the character is within one of the ranges of ASCII codes for hexadecimal digits; to obtain the character's value; and finally to apply positional weighting to that digit, as done previously (Figure 4-3). Allowing for mixed case, hexadecimal digits have three ranges: 0 9, A F, and a f. Since parallel compare instructions are typically tests against zero, we cannot use a sequence of them to determine if a value is in a given range. Therefore we build the range checks from pairs of regular comparisons, using predicate register p8 to flag cases where a character is not within one of the three tested ranges. The two alphabetic ranges are conveniently collapsed into one by use of a complemented mask (~0x20) that forces lowercase a z to uppercase A Z (see hex codes in Table 2-3). Once we have determined that a character is a valid hexadecimal digit, its numeric equivalent must be determined from its ASCII representation. For the numerals 0 9, the xor instruction with a mask of 0x30 toggles off bits <5:4>, leaving the binary numeric value. For the letters A-F, an add instruction with a suitable negative immediate value ( 0x37 = 0d55) as a mask is used to convert the ASCII code for A to the value 10, B to 11, and so forth. This method with a different mask could have also been used for the numerals, though it is not as "elegant." Figure 6-1 HEXNUM2: Illustrating masking with logical instructions// HEXNUM2 Number Conversion // This program will convert the positive number expressed // by hexadecimal digits stored as a string beginning // at H2 into a value in register r20 for inspection. .data // Declare storage .align 8 // Desired alignment H2: stringz "3a2" // Number to convert .text // Section for code .align 32 // Desired alignment .global main // These three lines .proc main // mark the mandatory main: // 'main' program entry .body // Now we really begin... first: mov r20 = 0 // Gr20 = value computed addl r14 = @gprel(H2),gp;; // Point to storage more: ld1 r21 = [r14],1;; // Get a character; bump cmp.eq p6,p0 = r21,r0 // Null code marks end (p6) br.cond.spnt.few done;; // of our work cmp.ge p6,p7 = 0x39,r21;; // See if <=9 (p6) cmp.le p6,p8 = 0x30,r21;; // See if >=0 (p6) xor r21 = 0x30,r21 // Value of numeral (p7) and r21 = ~0x20,r21;; // Force a-z to A-Z (p7) cmp.le p7,p8 = 0x41,r21;; // See if >=A (p7) cmp.ge p7,p8 = 0x46,r21 // See if <=F (p8) br.cond.spnt.few bad;; // None of the above (p7) add r21 = -(0x41-0xa),r21;; // Get digit value shladd r20 = r20,4,r21 // Value = 16*prev + next br.cond.sptk.few more // Go back for more bad: // Could handle errors... // Inspect r20 here... done: mov r8 = 0 // Signal all is normal br.ret.sptk.many b0;; // Back to command line .endp main // Mark end of procedure When you study this example at the computer, you should monitor the contents of registers r21 and r20. Watch the values in those registers change as you step through the loop until it exits to the line at done. You will see the masking, shifting, and accumulation. The final numeric value in register r20 should be 930. |