//---------------------------------------------------------------- // sprintf.s // // Here we have written our own (simplified) implementation // for the customary 'sprintf()' library-function: // // int sprintf( char *dst, char *fmt, ... ); // // By the end of this course you will be able to understand // this code -- but you are unlikely to understand it now. // // assemble using: $ as sprintf.s -o sprintf.o // // programmer: ALLAN CRUSE // written on: 23 JAN 2005 // revised on: 02 JAN 2009 -- for execution in 64-bit mode //---------------------------------------------------------------- .section .text numeral: .ascii "0123456789ABCDEF" # our translation-table sprintf: push %rbp # preserve frame-pointer mov %rsp, %rbp # setup local stack-frame sub $8, %rsp # create space for radix push %rax # preserve cpu registers push %rbx push %rcx push %rdx push %rsi push %rdi mov 16(%rbp), %rdi # dst parameter into RDI mov 24(%rbp), %rsi # fmt parameter into RSI mov $0, %rcx # initial argument-index cld # use forward processing again: cmpb $0, (%rsi) # test: null-terminator? je finish # yes, we are finished cmpb $'%', (%rsi) # test: format-escape? je escape # yes, insert numerals movsb # else copy the character jmp again # and go back for another escape: inc %rsi # skip past escape-code lodsb # and fetch escape-type cmpb $'d', %al # wanted decimal-format? movq $10, -8(%rbp) # yes, use 10 as the radix je do_tx # convert number to string cmpb $'X', %al # wanted hexadecimal-format? movq $16, -8(%rbp) # yes, use 16 as the radix je do_tx # convert number to string jmp errorx # otherwise return error do_tx: mov $numeral, %rbx # point RBX to numeral-list mov 32(%rbp,%rcx,8), %eax # get next argument in RAX inc %rcx # and advance argument-index push %rcx # preserve argument-index xor %rcx, %rcx # initialize digit-counter nxdiv: xor %rdx, %rdx # extend dividend to quadword divq -8(%rbp) # divide by selected radix push %rdx # push remainder onto stack inc %rcx # and increment digit-count or %rax, %rax # test: quotient was zero? jnz nxdiv # no, another digit needed nxdgt: pop %rax # saved remainder into EAX xlat %cs:(%rbx) # convert number to numeral stosb # store numeral in buffer loop nxdgt # go get the next remainder pop %rcx # recover the argument-index jmp again # and resume copying format finish: sub 16(%rbp), %rdi # compute output length mov %rdi, 16(%rbp) # and save it on stack jmp return # then exit this function errorx: movq $-1, 16(%rbp) # store error-indicator return: pop %rdi # restore saved registers pop %rsi pop %rdx pop %rcx pop %rbx pop %rax mov 16(%rbp), %rax # copy return-value to EAX mov %rbp, %rsp # discard temporary storage pop %rbp # restore saved frame-pointer ret # return control to caller .global sprintf # make entry-point public .end # ignore everything beyond