; Listing 9-8 ; ; Extended-precision signed integer ; to string function option casemap:none nl = 10 .const ttlStr byte "Listing 9-8", 0 fmtStr1 byte "otoStr(0): string=%s", nl, 0 fmtStr2 byte "otoStr(1234567890): string=%s", nl, 0 fmtStr3 byte "otoStr(2147483648): string=%s", nl, 0 fmtStr4 byte "otoStr(4294967296): string=%s", nl, 0 fmtStr5 byte "otoStr(FFF...FFFF): string=%s", nl, 0 .data buffer byte 40 dup (?) b0 oword 0 b1 oword -1 b2 oword -2147483648 b3 oword -4294967296 ; Largest oword value (unsigned) ; (decimal=170,141,183,460,469,231,731,687,303,715,884,105,727): b4 oword 07FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFh .code externdef printf:proc ; Return program title to C++ program: public getTitle getTitle proc lea rax, ttlStr ret getTitle endp ; DivideBy10- ; ; Divides "divisor" by 10 using fast ; extended-precision division algorithm ; that employs the div instruction. ; ; Returns quotient in "quotient". ; Returns remainder in rax. ; Trashes rdx. ; ; RCX - points at oword dividend and location to ; receive quotient ten qword 10 DivideBy10 proc parm equ <[rcx]> xor edx, edx mov rax, parm[8] div ten mov parm[8], rax mov rax, parm div ten mov parm, rax mov eax, edx ;Remainder (always 0..9!) ret DivideBy10 endp ; Recursive version of otoStr. ; A separate "shell" procedure calls this so that ; this code does not have to preserve all the registers ; it uses (and DivideBy10 uses) on each recursive call. ; ; On entry: ; Stack contains oword parameter ; RDI- contains location to place output string ; ; Note: this function must clean up stack (parameters) ; on return. rcrsvOtoStr proc value equ <[rbp+16]> remainder equ <[rbp-8]> push rbp mov rbp, rsp sub rsp, 8 lea rcx, value call DivideBy10 mov remainder, al ; If the quotient (left in value) is not 0, recursively ; call this routine to output the HO digits. mov rax, value or rax, value[8] jz allDone mov rax, value[8] push rax mov rax, value push rax call rcrsvOtoStr allDone: mov al, remainder or al, '0' mov [rdi], al inc rdi leave ret 16 ;Remove parms from stack rcrsvOtoStr endp ; Nonrecursive shell to the above routine so we don't bother ; saving all the registers on each recursive call. ; ; On entry: ; ; RDX:RAX- contains oword to print ; RDI- buffer to hold string (at least 40 bytes) otostr proc push rax push rdx push rdi ; Special-case zero: test rax, rax jnz not0 test rdx, rdx jnz not0 mov byte ptr [rdi], '0' inc rdi jmp allDone not0: push rdx push rax call rcrsvOtoStr ; Zero-terminate string before leaving allDone: mov byte ptr [rdi], 0 pop rdi pop rdx pop rax ret otostr endp ; i128toStr- ; Converts a 128-bit signed integer to a string ; ; Inputs; ; RDX:RAX- signed integer to convert ; RDI- pointer to buffer to receive string i128toStr proc push rax push rdx push rdi test rdx, rdx ;Is number negative? jns notNeg mov byte ptr [rdi], '-' inc rdi neg rdx ;128-bit negation neg rax sbb rdx, 0 notNeg: call otostr pop rdi pop rdx pop rax ret i128toStr endp ; Here is the "asmMain" function. public asmMain asmMain proc push rbp mov rbp, rsp sub rsp, 64 ;Shadow storage push rdi ; Because all the (x)toStr functions preserve RDI, ; we only need to do the following once: lea rdi, buffer ; Convert b0 to a string and print the result: mov rax, qword ptr b0 mov rdx, qword ptr b0[8] call i128toStr lea rcx, fmtStr1 lea rdx, buffer call printf ; Convert b1 to a string and print the result: mov rax, qword ptr b1 mov rdx, qword ptr b1[8] call i128toStr lea rcx, fmtStr2 lea rdx, buffer call printf ; Convert b2 to a string and print the result: mov rax, qword ptr b2 mov rdx, qword ptr b2[8] call i128toStr lea rcx, fmtStr3 lea rdx, buffer call printf ; Convert b3 to a string and print the result: mov rax, qword ptr b3 mov rdx, qword ptr b3[8] call i128toStr lea rcx, fmtStr4 lea rdx, buffer call printf ; Convert b4 to a string and print the result: mov rax, qword ptr b4 mov rdx, qword ptr b4[8] call i128toStr lea rcx, fmtStr5 lea rdx, buffer call printf pop rdi leave ret ;Returns to caller asmMain endp end