[30] <§§2.7, 2.8> Write a program in MIPS assembly language to convert an ASCII decimal string to an integer. Your program should expect register $a0 to hold the address of a null-terminated string containing some combination of the digits 0 through 9. Your program should compute the integer value equivalent to this string of digits, then place the number in register $v0.
Your program need not handle negative numbers. If a nondigit character appears anywhere in the string, your program should stop with the value -1 in register $v0.
For example, if register $a0 points to a sequence of three bytes 50, 52, 0 (the null-terminated string “24”), then when the program stops, register $v0 should contain the value 24
面倒なのでCで書きます。
#include <stdio.h> #include <stdlib.h> #include <assert.h> #include <limits.h> int s2c(const char *s); #define RET_ERR (-1) const char *test_data[] = {"24", "0", "412", "3562", "1234567890", NULL}; const char *error_data[] = {"-24", "abc", "", "3562a", NULL}; //const char *ovflow_data[] = {"214748364", "2147483647", NULL}; int s2c(const char *s) { const char *p; int v, i; char c; assert(s!=NULL); if(*s=='\0') { return RET_ERR; } v = 0; for(p=s;*p!='\0';p++){ c = *p; if((c < '0') || ( c > '9')) { return RET_ERR; } i = c - '0'; assert(i>=0); assert(i<=9); assert(v>=0); assert(v<(INT_MAX / 10)); v *= 10; v += i; } return v; } int main(void) { int i; int golden; int ans; int err; err = 0; for(i=0;test_data[i]!=NULL;i++){ golden = atoi(test_data[i]); ans = s2c(test_data[i]); if(ans == golden) { printf("OK:%d\n", ans); } else { err++; printf("ERROR:golden = %d, ans = %d\n", golden, ans); } } for(i=0;error_data[i]!=NULL;i++){ ans = s2c(error_data[i]); if(ans == RET_ERR) { printf("OK:%d\n", ans); } else { err++; printf("ERROR:data = \"%s\", ans = %d\n", error_data[i], ans); } } if(err!=0){ printf("ERROR %d found\n", err); } else { printf("ALL DATA PASSED\n"); } return 0; }
assert外して、最適化かけたコードがこれです。
s2c: move $3,$4 lbu $4,0($4) beq $4,$0,.L13 li $2,-1 # 0xffffffffffffffff move $5,$3 move $6,$0 sll $9,$6,2 .L12: addiu $4,$4,-48 addu $8,$9,$6 andi $7,$4,0x00ff sll $2,$8,1 sltu $3,$7,10 addiu $5,$5,1 beq $3,$0,.L10 addu $6,$2,$4 lbu $4,0($5) bne $4,$0,.L12 sll $9,$6,2 move $2,$6 .L13: j $31 nop .L10: j $31 li $2,-1 # 0xffffffffffffffff
.L12周辺で、'0'から'9'に入っているかの判定処理($3,$4,$7)と10倍を作る処理($2,$6,$8,$9)が、パイプラインを乱さないように交代に並んでいるのが素晴らしいです。