Solving Pascal's Triangle (nCk) in Assembly Recursion - linux

i am currently stuck with getting the answer from a combination problem. My basecase works just fine. I think the problem is at evaluating combination(n-1,k) and then evaluating combination(n-1,k-1).
Here's my code: n and k are inputs from user.
sub esp, 2
push word[n]
push word[k]
call combi
pop word[ans] ;store yung combi value sa ans
;convert to ascii string value
add word[ans], 30h
;print answer
mov eax, 4
mov ebx, 1
mov ecx, ans
mov edx, 2
int 80h
jmp exit
combi:
mov ebp, esp
mov ax, word[ebp+4] ;ax = k
cmp [ebp+6], ax ;if (n==k)
je basecase
cmp word[ebp+4],0 ;cmp k = 0
je basecase
;combi(n-1,k)
mov ax, [ebp+6] ; ax = n
mov bx, [ebp+4] ; bx = k
dec ax ;n-1
;execute again
sub esp, 2
push ax
push bx
call combi
pop cx ;stores to cx popped value combi(n-1,k)
mov ebp, esp ;update pointers
;combi(n-1,k-1)
push ax
dec bx
push bx
call combi
pop dx ;stores to dx popped value combi(n-1,k-1)
mov ebp, esp ;update pointers
add cx, dx ;combi(n-1,k) + combi(n-1,k-1)
mov word[ebp+8], cx
jmp combi_exit
basecase:
mov word[ebp+8], 1
combi_exit:
ret 4
Hoping for your kind responses and brilliant ideas! Thank you!

To fix your recursion, the middle part of combi: has a problem:
...
call combi
pop cx ;stores to cx popped value combi(n-1,k)
;* ^ this freed the allocated space for result
mov ebp, esp ;update pointers
;* not needed, as you will not use EBP right now, and next call destroys it
;combi(n-1,k-1)
push ax
;* ^ pushing first argument, but no stack space reserved for result
dec bx
push bx
call combi
...
To fix you can adjust that part to:
EDIT: this will not work correctly for 2+ deep recursion, as you don't preserve registers as needed, the whole recursion architecture requires more care and I would opt simply to rewrite it with simpler design in the first place, than fixing all these minor problems. This "fix" will at least stop the segfaulting.
...
call combi
mov cx,[esp] ;stores to cx value combi(n-1,k)
;* ^ keeps the stack space reserved (not popping)
;combi(n-1,k-1)
push ax
...
There's of course the other problem with your output being correct only for single digit numbers, but just search stack overflow and the tag [x86] info for those, not going to repeat it here.
BTW, this IMO stems from the unfortunate overcomplicated usage stack, do you have some particular reason why you follow such complex calling convention? How about some custom fastcall-like giving arguments and results in registers? It's also much more performant, but for me personally it's also easier to keep track of things and process stack correctly. I may add my own variant later to this answer, if I will try...
EDIT: full working example with register calling convention:
file: so_32b_pascal_triangle.asm
section .text
global _start
_start:
mov ecx,5 ; n
mov edx,2 ; k
call combi
; return to linux with sys_exit(result)
mov ebx,eax
mov eax,1
int 80h
; ECX = n, EDX = k, returns result in EAX, no other register modified
; (_msfastcall-like convention, but extended to preserve ECX+EDX)
combi: ; c(n, k) = c(n-1, k-1) + c(n-1, k), c(i, 0) = c(i, i) = 1
test edx,edx ; k == 0
je .basecases ; c(i, 0) = 1
cmp edx,ecx ; k == n
je .basecases ; c(i, i) = 1
; 0 < k < n case:
dec ecx ; n-1
call combi ; EAX = c(n-1, k)
push esi
mov esi,eax ; keep c(n-1, k) in ESI
dec edx ; k-1
call combi ; EAX = c(n-1, k-1)
add eax,esi ; EAX = c(n-1, k-1) + c(n-1, k)
; restore all modified registers
pop esi
inc ecx
inc edx
ret ; return result in EAX
.basecases:
mov eax,1
ret
compilation + run + result displayed:
...$ nasm -f elf32 -F dwarf -g so_32b_pascal_triangle.asm -l so_32b_pascal_triangle.lst -w+all
...$ ld -m elf_i386 -o so_32b_pascal_triangle so_32b_pascal_triangle.o
...$ ./so_32b_pascal_triangle ; echo $?
10
...$
EDIT:
And for my own curiosity, tried to call it from C-ish C++ code (to verify the fastcall convention is working as expected even when interoperability with C/C++ is required):
The so_32b_pascal_triangle.asm file has same combi: code, but the beginning is modified (added global, removed _start):
section .text
global combi
; ECX = n, EDX = k, returns result in EAX, no other register modified
; (fastcall-like convention, but extended to preserve ECX+EDX)
combi: ; c(n, k) = c(n-1, k-1) + c(n-1, k), c(i, 0) = c(i, i) = 1
...
file so_32b_pascal_triangle_Cpp.cpp:
#include <cstdio>
#include <cstdint>
extern "C" __attribute__ ((fastcall)) uint32_t combi(uint32_t n, uint32_t k);
int main()
{
for (uint32_t n = 0; n < 10; ++n) {
printf("%*c", 1+2*(10-n), ' ');
for (uint32_t k = 0; k <= n; ++k) {
printf("%4d", combi(n, k));
// 4-char width formatting - works for 3 digit results max.
}
printf("\n");
}
}
Build + test:
$ nasm -f elf32 -F dwarf -g so_32b_pascal_triangle.asm -l so_32b_pascal_triangle.lst -w+all
$ g++ -std=c++17 -c -m32 -O3 -Wall -Wpedantic -Wextra so_32b_pascal_triangle_Cpp.cpp
$ g++ -m32 so_32b_pascal_triangle*.o -o so_32b_pascal_triangle
$ ./so_32b_pascal_triangle
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
1 7 21 35 35 21 7 1
1 8 28 56 70 56 28 8 1
1 9 36 84 126 126 84 36 9 1

Related

Getting seg fault for writing procedures in x86 32-bit compiled using NASM

Ackermann’s function A(m,n), where m ≥ 0 and n ≥ 0, is defined as
A(0, n) = n + 1
A(m + 1, 0) = A(m, 1)
A(m + 1, n + 1) = A(m,A(m+1,n))
I am trying to write the code for the above function but I am getting seg fault for m=1,n=1. What am I doing wrong?? I am using NASM assembly.
%include "io.mac"
.DATA
str1 db "Enter the value of m : ",0
str2 db "Enter the value of n : ",0
str3 db "A(m,n) = ",0
.UDATA
m resd 1
n resd 1
.CODE
.STARTUP
PutStr str1
GetLInt [m]
mov AX,[m]
PutStr str2
GetLInt [n]
mov BX,[n]
push AX
push BX
call ackermanFunc
jmp end
ackermanFunc:
enter 0,0
mov AX,[EBP+10]
mov BX,[EBP+8]
cmp AX,0
je case1
cmp BX,0
je case2
jmp case3
case1:
mov CX,BX
inc CX
leave
ret 4
case2:
dec AX
push AX
push 1
call ackermanFunc
leave
ret 4
case3:
dec BX
push AX
push BX
call ackermanFunc
dec AX
mov BX,CX
push AX
push BX
call ackermanFunc
leave
ret 4
end:
PutStr str3
PutInt CX
nwln
.EXIT

VC inline asm - shifting with rcl (rcr)

I'm using the following function to shift the data of a vector by 1 bit (mult by 2):
vec shl(vec n) {
n.resize(n.size() + 1, 0);
unsigned int* adr = n.data();
unsigned int s = n.size();
_asm {
clc
mov ecx, 0
mov edx, dword ptr [adr]
nloop:
mov eax, dword ptr[edx + ecx * 4]
rcl eax, 1
mov dword ptr [edx + ecx * 4], eax
inc ecx
jc carryisset ; check carry - breakpoint
jmp nocarry ; ~ breakpoint
carryisset : ; ~ breakpoint
jmp nocarry ; ~ breakpoint
nocarry: ; ~ breakpoint
cmp ecx, dword ptr [s]
jl nloop
};
return n;
};
So, I've read that rcl uses the carry bit and add it to the high bit. But when the carry bit is not set according to the debugger, the rcl continues adding it to eax.
For example:
#include <iostream>
#include <vector>
typedef std::vector<unsigned int> vec;
const unsigned int uint_max = (unsigned int)(~0);
vec n1 = { uint_max, 2, 2, 1, 0, 0, 0 };
vec n2
int main() {
n2 = shl(n1);
for (auto i : n2)
std::cout << i << " ";
return 0;
};
Output:
4294967294 5 5 3 1 1 1 1
Stepping through the code with debugger:
loop: first iteration (ecx = 0)
eax <- uint_max
eax <- rotate left with carry (rcl)
now eax is uint_max - 1
jumps to carryisset (with jc), so there is a carry
loop: second iteration (ecx = 1)
eax <- 2
eax <- rotate left with carry (rcl)
now eax is 2 << 2 + (carry)1 = 5
jumps to nocarry (with jc), so there is no carry
loop: third iteration (ecx = 2)
eax <- 2
eax <- rotate left with carry (rcl)
now eax is 2 << 2 + carry (should be 0, not set), but eax gives 5 too, like there were carry.
jumps to nocarry (with jc), so there is no carry (at least according to jc)
...ect
So, there is no carry after the first iteration in this case, but the carry does not 'reset'.
This implementation came from an SO post Large binary shifts in 8086 assembly? (accepted answer):
First, make sure the carry flag is zero. Then:
1. Pull 4 bytes into a register
2. RCR - in my case RCL
3. Write back out
4. Repeat with the next 4 bytes
However the carry bit is always on when I rotate left (or tried with right, same results: in case of vec(2,0,0,0,0...) it is vec(1, uint_max/2 + 1, uint max/2 + 1, ...))
ps: I made a working shift avoiding the carry and checking the highest bit, but it is a overcomplicated I think:
_asm {
clc
mov edx, dword ptr [adr]
xor ebx, ebx
xor ecx, ecx
xor eax, eax
nloop:
mov eax, dword ptr[edx + ecx * 4]
push edx
mov edx, ebx
mov ebx, eax
and ebx, 0x80000000
shr ebx, 31
shl eax, 1
add eax, edx
pop edx
mov dword ptr [edx + ecx * 4], eax
inc ecx
xor eax, eax
cmp ecx, dword ptr [s]
jl nloop
};
What is the problem with the first code, how to use rcl and rcr for shifting?
(Thanks to Hans. See the comment.)
The working code:
clc
mov ecx, 0
mov edx, dword ptr[adr]
nloop:
pushf
cmp ecx, dword ptr [s]
je fin
popf
mov eax, dword ptr[edx + ecx * 4]
rcl eax, 1
mov dword ptr[edx + ecx * 4], eax
inc ecx
jmp nloop
fin:
popf
I clear the flags first. In the main loop, pushf the flags just for a cmp, after popf them. For this, I moved the compare to the begin of the loop. For the fin just popf the the flags after the jump to avoid ESP errors.

NASM Assembly recursive fibonacci

Learning NASM Assembly on 32-bit Ubuntu.
I've been learning about recursive functions. I just did factorial, with your help here: Understanding recursive factorial function in NASM Assembly
Watching the code, I thought that maybe I could quickly implement fibonacci as well, using almost the same code. Here is the code, assuming that the parameter is always greater than 0:
SECTION .text
global main
main:
; -----------------------------------------------------------
; Main
; -----------------------------------------------------------
push 6
call fibonacci
mov [ECX],EAX
add byte [ECX],'0'
mov EDX,1
call print
; -----------------------------------------------------------
; Exit
; -----------------------------------------------------------
mov EAX,1
int 0x80
; -----------------------------------------------------------
; Fibonacci recursivo: f(n) = f(n - 1) + f(n - 2)
; -----------------------------------------------------------
fibonacci:
push EBP ; Retrieve parameter and put it
push EBX ; Save previous parameter
mov EBP,ESP ; into EBX register
add EBP,12 ;
mov EBX,[EBP] ; EBX = Param
cmp EBX,1 ; Check for base case
jle base ; It is base if (n <= 1)
dec EBX ; Decrement EBX to put it in the stack
push EBX ; Put (EBX - 1) in stack
inc EBX ; Restore EBX
call fibonacci ; Calculate fibonacci for (EBX - 1)
mov ESI,EAX ; EAX = (EAX + EBX)
pop EBX ; Retrieve EBX from the stack
sub EBX,2 ; Decrement EBX to put it in the stack
push EBX ; Put (EBX - 2) in stack
add EBX,2 ; Restore EBX
call fibonacci ; Calculate fibonacci for (EBX - 2)
mov EDX,EAX ; EAX = (EAX + EBX)
pop EBX ; Retrieve EBX from the stack
add ESI,EDX
mov EAX,ESI
jmp end
base: ; Base case
mov EAX,1 ; The result would be 1
end:
pop EBX ; Restore previous parameter
pop EBP ; Release EBP
ret
It is a bit crude. I calculate fibonacci for (parameter - 1), then I do it again for (parameter - 2), and just add them up and put them into EAX.
It doesn't work:
2 => 2
3 => 3
4 => 4
5 => 4
Fortunately I fixed the segmentation fault errors, but I probably broke something else doing that. Now I don't see what's the problem. Can you tell me why am I getting the wrong values?
One particular observation is that, for some reason, doing mov ECX,EAX gave me a segmentation fault error. That's why I used ESI instead. I'm not sure why, but I guess that it is related.
Whenever you're dealing with recursion, you have to be very careful about what the next layer in the recursive chain will do to the state of the current layer (e.g. register values). I'd suggest rewriting the function as follows:
fibonacci:
push EBP ; Retrieve parameter and put it
push EBX ; Save previous parameter
mov EBP,ESP ; into EBX register
add EBP,12 ;
mov EBX,[EBP] ; EBX = Param
cmp EBX,1 ; Check for base case
jle base ; It is base if (n <= 1)
lea ecx,[ebx-1]
push ecx ; push N-1
call fibonacci ; Calculate fibonacci for (EBX - 1)
pop ecx ; remove N-1 off the stack
push eax ; save the result of fibonacci(N-1)
lea ecx,[ebx-2]
push ecx ; push N-2
call fibonacci ; Calculate fibonacci for (EBX - 2)
pop ecx ; remove N-2 off the stack
pop ecx ; ecx = fibonacci(N-1)
add eax,ecx ; eax = fibonacci(N-2) + fibonacci(N-1)
jmp end
base: ; Base case
mov EAX,1 ; The result would be 1
end:
pop EBX ; Restore previous parameter
pop EBP ; Release EBP
ret

nasm infinite loop with FPU

i'm trying to create a small nasm program which do this operation in floating point
while(input <= 10^5) do
begin
input = input * 10
i = i - 1
end
the equivilant program in nasm is as following
section .data
input: resd 1
n10: dd 0x41200000 ; 10
_start:
mov eax, 0x43480000 ; eax = 200
mov dword [input], eax ; input = eax = 200
mov edx, 0x49742400 ; 10^5
; %begin
mov ecx, 0 ; i = 0
jmp alpha
alpha:
cmp [input], edx ; input <= 10^5
jle _while
jmp log2
_while:
fld dword [input] ; input
fmul dword [n10] ; input * 10
fst dword [input] ; input = input
dec ecx ; i = i - 1
jmp alpha
the _while loop is iterating infinitely
ecx / i gards always the same value = 0 (it is sepposed to be 0) and doesn't decrement
This works for me (tested in DosBox):
org 0x100
bits 16
_start:
mov dword [input], __float32__(99.0)
mov edx, __float32__(10000.0)
mov ecx, 0 ; i = 0
jmp alpha
alpha:
cmp [input],edx ; input <= 10^5
jle _while
jmp log2
_while:
fld dword [input] ; input
fmul dword [n10] ; input * 10
fstp dword [input] ; input = input
inc ecx ; i = i - 1
jmp alpha
log2:
; print the value of cl
mov dl,cl
add dl,'0'
mov ah,2
int 21h
; Exit to DOS
mov ah,0x4c
int 21h
n10: dd 10.0
input: resd 1
Note the bits 16 which tells nasm that 16-bit operands are the default and that instructions using 32-bit operands should be prefixed. Without this your code would be seen as gibberish if you try to execute it in a real-mode environment.It's possible that you'll need to use bits 32 instead, depending on your target environment.
Also note the use of floating-point literals rather than hex values (you had a typo in your code where you compared against 10^6 instead of 10^5).

How to print a number in assembly NASM?

Suppose that I have an integer number in a register, how can I print it? Can you show a simple example code?
I already know how to print a string such as "hello, world".
I'm developing on Linux.
If you're already on Linux, there's no need to do the conversion yourself. Just use printf instead:
;
; assemble and link with:
; nasm -f elf printf-test.asm && gcc -m32 -o printf-test printf-test.o
;
section .text
global main
extern printf
main:
mov eax, 0xDEADBEEF
push eax
push message
call printf
add esp, 8
ret
message db "Register = %08X", 10, 0
Note that printf uses the cdecl calling convention so we need to restore the stack pointer afterwards, i.e. add 4 bytes per parameter passed to the function.
You have to convert it in a string; if you're talking about hex numbers it's pretty easy. Any number can be represented this way:
0xa31f = 0xf * 16^0 + 0x1 * 16^1 + 3 * 16^2 + 0xa * 16^3
So when you have this number you have to split it like I've shown then convert every "section" to its ASCII equivalent.
Getting the four parts is easily done with some bit magic, in particular with a right shift to move the part we're interested in in the first four bits then AND the result with 0xf to isolate it from the rest. Here's what I mean (soppose we want to take the 3):
0xa31f -> shift right by 8 = 0x00a3 -> AND with 0xf = 0x0003
Now that we have a single number we have to convert it into its ASCII value. If the number is smaller or equal than 9 we can just add 0's ASCII value (0x30), if it's greater than 9 we have to use a's ASCII value (0x61).
Here it is, now we just have to code it:
mov si, ??? ; si points to the target buffer
mov ax, 0a31fh ; ax contains the number we want to convert
mov bx, ax ; store a copy in bx
xor dx, dx ; dx will contain the result
mov cx, 3 ; cx's our counter
convert_loop:
mov ax, bx ; load the number into ax
and ax, 0fh ; we want the first 4 bits
cmp ax, 9h ; check what we should add
ja greater_than_9
add ax, 30h ; 0x30 ('0')
jmp converted
greater_than_9:
add ax, 61h ; or 0x61 ('a')
converted:
xchg al, ah ; put a null terminator after it
mov [si], ax ; (will be overwritten unless this
inc si ; is the last one)
shr bx, 4 ; get the next part
dec cx ; one less to do
jnz convert_loop
sub di, 4 ; di still points to the target buffer
PS: I know this is 16 bit code but I still use the old TASM :P
PPS: this is Intel syntax, converting to AT&T syntax isn't difficult though, look here.
Linux x86-64 with printf
main.asm
default rel ; make [rel format] the default, you always want this.
extern printf, exit ; NASM requires declarations of external symbols, unlike GAS
section .rodata
format db "%#x", 10, 0 ; C 0-terminated string: "%#x\n"
section .text
global main
main:
sub rsp, 8 ; re-align the stack to 16 before calling another function
; Call printf.
mov esi, 0x12345678 ; "%x" takes a 32-bit unsigned int
lea rdi, [rel format]
xor eax, eax ; AL=0 no FP args in XMM regs
call printf
; Return from main.
xor eax, eax
add rsp, 8
ret
GitHub upstream.
Then:
nasm -f elf64 -o main.o main.asm
gcc -no-pie -o main.out main.o
./main.out
Output:
0x12345678
Notes:
sub rsp, 8: How to write assembly language hello world program for 64 bit Mac OS X using printf?
xor eax, eax: Why is %eax zeroed before a call to printf?
-no-pie: plain call printf doesn't work in a PIE executable (-pie), the linker only automatically generates a PLT stub for old-style executables. Your options are:
call printf wrt ..plt to call through the PLT like traditional call printf
call [rel printf wrt ..got] to not use a PLT at all, like gcc -fno-plt.
Like GAS syntax call *printf#GOTPCREL(%rip).
Either of these are fine in a non-PIE executable as well, and don't cause any inefficiency unless you're statically linking libc. In which case call printf can resolve to a call rel32 directly to libc, because the offset from your code to the libc function would be known at static linking time.
See also: Can't call C standard library function on 64-bit Linux from assembly (yasm) code
If you want hex without the C library: Printing Hexadecimal Digits with Assembly
Tested on Ubuntu 18.10, NASM 2.13.03.
It depends on the architecture/environment you are using.
For instance, if I want to display a number on linux, the ASM code will be different from the one I would use on windows.
Edit:
You can refer to THIS for an example of conversion.
I'm relatively new to assembly, and this obviously is not the best solution,
but it's working. The main function is _iprint, it first checks whether the
number in eax is negative, and prints a minus sign if so, than it proceeds
by printing the individual numbers by calling the function _dprint for
every digit. The idea is the following, if we have 512 than it is equal to: 512 = (5 * 10 + 1) * 10 + 2 = Q * 10 + R, so we can found the last digit of a number by dividing it by 10, and
getting the reminder R, but if we do it in a loop than digits will be in a
reverse order, so we use the stack for pushing them, and after that when
writing them to stdout they are popped out in right order.
; Build : nasm -f elf -o baz.o baz.asm
; ld -m elf_i386 -o baz baz.o
section .bss
c: resb 1 ; character buffer
section .data
section .text
; writes an ascii character from eax to stdout
_cprint:
pushad ; push registers
mov [c], eax ; store ascii value at c
mov eax, 0x04 ; sys_write
mov ebx, 1 ; stdout
mov ecx, c ; copy c to ecx
mov edx, 1 ; one character
int 0x80 ; syscall
popad ; pop registers
ret ; bye
; writes a digit stored in eax to stdout
_dprint:
pushad ; push registers
add eax, '0' ; get digit's ascii code
mov [c], eax ; store it at c
mov eax, 0x04 ; sys_write
mov ebx, 1 ; stdout
mov ecx, c ; pass the address of c to ecx
mov edx, 1 ; one character
int 0x80 ; syscall
popad ; pop registers
ret ; bye
; now lets try to write a function which will write an integer
; number stored in eax in decimal at stdout
_iprint:
pushad ; push registers
cmp eax, 0 ; check if eax is negative
jge Pos ; if not proceed in the usual manner
push eax ; store eax
mov eax, '-' ; print minus sign
call _cprint ; call character printing function
pop eax ; restore eax
neg eax ; make eax positive
Pos:
mov ebx, 10 ; base
mov ecx, 1 ; number of digits counter
Cycle1:
mov edx, 0 ; set edx to zero before dividing otherwise the
; program gives an error: SIGFPE arithmetic exception
div ebx ; divide eax with ebx now eax holds the
; quotent and edx the reminder
push edx ; digits we have to write are in reverse order
cmp eax, 0 ; exit loop condition
jz EndLoop1 ; we are done
inc ecx ; increment number of digits counter
jmp Cycle1 ; loop back
EndLoop1:
; write the integer digits by poping them out from the stack
Cycle2:
pop eax ; pop up the digits we have stored
call _dprint ; and print them to stdout
dec ecx ; decrement number of digits counter
jz EndLoop2 ; if it's zero we are done
jmp Cycle2 ; loop back
EndLoop2:
popad ; pop registers
ret ; bye
global _start
_start:
nop ; gdb break point
mov eax, -345 ;
call _iprint ;
mov eax, 0x01 ; sys_exit
mov ebx, 0 ; error code
int 0x80 ; край
Because you didn't say about number representation I wrote the following code for unsigned number with any base(of course not too big), so you could use it:
BITS 32
global _start
section .text
_start:
mov eax, 762002099 ; unsigned number to print
mov ebx, 36 ; base to represent the number, do not set it too big
call print
;exit
mov eax, 1
xor ebx, ebx
int 0x80
print:
mov ecx, esp
sub esp, 36 ; reserve space for the number string, for base-2 it takes 33 bytes with new line, aligned by 4 bytes it takes 36 bytes.
mov edi, 1
dec ecx
mov [ecx], byte 10
print_loop:
xor edx, edx
div ebx
cmp dl, 9 ; if reminder>9 go to use_letter
jg use_letter
add dl, '0'
jmp after_use_letter
use_letter:
add dl, 'W' ; letters from 'a' to ... in ascii code
after_use_letter:
dec ecx
inc edi
mov [ecx],dl
test eax, eax
jnz print_loop
; system call to print, ecx is a pointer on the string
mov eax, 4 ; system call number (sys_write)
mov ebx, 1 ; file descriptor (stdout)
mov edx, edi ; length of the string
int 0x80
add esp, 36 ; release space for the number string
ret
It's not optimised for numbers with base of power of two and doesn't use printf from libc.
The function print outputs the number with a new line. The number string is formed on stack. Compile by nasm.
Output:
clockz
https://github.com/tigertv/stackoverflow-answers/tree/master/8194141-how-to-print-a-number-in-assembly-nasm

Resources