printfの自作
Posted feedbacks - Other
Mac OS X (PowerPC 32bit) アセンブリで、大雑把ですが。%%, %s, %d, %fのみ実装しています。精度指定はできません。
浮動小数点数の命令がよくわからず、小数点を出力するのに「10倍した浮動小数点数の一の位を出力する」ことを精度分やってます。
可変長引数はコンパイラ依存なので、次の専用の型を想定することにしました。
typedef union dk_va {
char *sval;
int ival;
float fval;
} dk_va, *dk_va_list_t;
int dk_sprintf(char *dest, const char *format, dk_va_list_t args);
例:
dk_va args[3]; int i; args[0].sval = "hello"; args[1].ival = 1510; args[2].fval = -384.148388; i = dk_sprintf(dest, "%%, %s, %d, %f", args);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | ;; ------------------------------------------------
;; mysprintf.s for Mac OS X (PowerPC 32bit)
;; % as -o dk_sprintf.o dk_sprintf.s
;; ------------------------------------------------
.machine ppc
.globl _dk_sprintf
;; typedef union dk_va {
;; char *sval;
;; int ival;
;; float fval;
;; } dk_va, *dk_va_list_t;
;;
;; int dk_sprintf(char *dest, const char *format, dk_va_list_t args)
;; r3: char *dest
;; r4: const char *format
;; r5: dk_va_list_t args
;;
;; r7(buf): dest の書き込み用コピー
;; r8(c): スキャンする文字
;; r9(flag): 前の文字が % なら 1
_dk_sprintf:
;; 初期化
mr r7, r3
li r9, 0
_scan:
;; format から一文字読み込む
lbz r8, 0(r4) ; c = *format
;; ディレクティブ
cmpli cr7, r9, 1 ; flag == 1
beq cr7, _scan_directive
cmpli cr7, r8, 37 ; c == '%'
beq cr7, _switch_directive
;; format から読み込んだ文字を書き込む
stb r8, 0(r7) ; *buf = c
addi r7, r7, 1 ; buf++
;; 文字列の終端
cmpli cr7, r8, 0 ; c == 0
beq cr7, _dk_sprintf_return
;; 繰り返し
addi r4, r4, 1 ; format++
b _scan
_switch_directive:
li r9, 1 ; flag = 1
addi r4, r4, 1 ; format++
b _scan
_scan_directive:
li r9, 0 ; flag = 0
;; %%
cmpli cr7, r8, 37 ; c == '%'
beq cr7, _write_escape
;; %s
cmpli cr7, r8, 115 ; c == 's'
beq cr7, _write_s
;; %d
cmpli cr7, r8, 100 ; c == 'd'
beq cr7, _write_d
;; %f
cmpli cr7, r8, 102 ; c == 'f'
beq cr7, _write_f
;; 未定義のディレクティブ
li r10, 37 ; '%'
stb r10, 0(r7) ; *buf = '%'
stb r8, 1(r7) ; *(buf+1) = c
addi r7, r7, 2 ; buf += 2
addi r4, r4, 1 ; format++
b _scan
;; %%
_write_escape:
li r10, 37 ; '%'
stb r10, 0(r7) ; *buf = '%'
addi r7, r7, 1 ; buf++
addi r4, r4, 1 ; format++
b _scan
;; %s
_write_s:
lwz r10, 0(r5) ; args->sval
addi r5, r5, 4
addi r4, r4, 1 ; format++
__write_s:
lbz r11, 0(r10) ; s = args->sval
cmpli cr7, r11, 0
beq cr7, _scan
stb r11, 0(r7) ; *buf = s
addi r7, r7, 1 ; buf++
addi r10, r10, 1 ; args->sval++
b __write_s
;; %d
_write_d:
lwz r10, 0(r5) ; i = args->ival
addi r5, r5, 4
addi r4, r4, 1 ; format++
mflr r16 ; リンクレジスタをスタックに退避
bl _write_digit
mtlr r16 ; リンクレジスタをスタックから復帰
b _scan
;; %f
;; r17: 精度
;; f0: 元の数値
;; f1: 整数部
;; f5: 0
;; f6: 10
_write_f:
;; 定数の準備など
lis r13, hi16(zero)
addi r13, r13, lo16(zero)
lfs f5, 0(r13) ; 0
lis r13, hi16(ten)
addi r13, r13, lo16(ten)
lfs f6, 0(r13) ; 10
li r17, 6 ; 精度
lfs f0, 0(r5) ; f = args->fval
addi r5, r5, 4
addi r4, r4, 1 ; format++
;; 整数部
fctiwz f1, f0 ; (int)f, ビットは整数表現になる
stfd f1, -8(r1) ; スタックを通して
lwz r10, -4(r1) ; 汎用レジスタに入れる
mflr r16
bl _write_digit
mtlr r16
;; 区切り
li r14, 46 ; '.'
stb r14, 0(r7) ; *buf = '.'
addi r7, r7, 1 ; buf++
fabs f2, f0
li r11, 10
;; 小数部
_write_fpointpart:
fmuls f2, f2, f6
fctiwz f3, f2
stfd f3, -8(r1)
lwz r10, -4(r1)
divwu r12, r10, r11
mullw r12, r12, r11
sub r10, r10, r12
addi r10, r10, 48 ; ASCII
stb r10, 0(r7)
addi r7, r7, 1 ; buf++
subi r17, r17, 1
cmpli cr7, r17, 0
bgt cr7, _write_fpointpart
b _scan
;; 整数を10進数で書き込む
;; r10: 書き込む整数
_write_digit:
li r11, 25000 ; fig, 桁
mulli r11, r11, 4
li r14, 10 ; 桁の除算用
li r15, 0 ; 残りを数字をすべて表示するか
;; 負の数
cmpi cr7, r10, 0
bgt cr7, _write_each_digit
li r12, 45 ; '-'
stb r12, 0(r7) ; *buf = '-'
addi r7, r7, 1 ; buf++
mulli r10, r10, -1 ; i = -i
;; 上の桁から順に出力する
_write_each_digit:
divw r12, r10, r11 ; d = i / fig
mullw r13, r11, r12 ; rem = i - fig * d
sub r10, r10, r13
divw r11, r11, r14 ; fig /= 10
cmpli cr7, r15, 0
cmpli cr6, r12, 0 ; d == 0
crand 2, 30, 26 ; cr0[eq] = cr7[eq] && cr6[eq]
beq cr0, _write_each_digit
li r15, 1
addi r12, r12, 48 ; ASCIIコードにする
stb r12, 0(r7) ; *buf = s
addi r7, r7, 1 ; buf++
;; 最後の桁
cmpli cr7, r11, 1 ; fig = 1
bgt cr7, _write_each_digit
mr r12, r10
addi r12, r12, 48 ; ASCIIコードにする
stb r12, 0(r7) ; *buf = s
addi r7, r7, 1 ; buf++
blr ; 終了
_dk_sprintf_return:
sub r3, r7, r3 ; return buf - dest
subi r3, r3, 1
blr
.data
.align 2
zero:
.single 0.0
.align 2
ten:
.single 10
.align 2
|


yappy
#4119()
[
C
]
Rating-4/18=-0.22
Rating-4/18=-0.22-0+
[ reply ]