From 655d184ceacd8ea943add55d52ececd76bb720c9 Mon Sep 17 00:00:00 2001 From: Gentoo Date: Sat, 27 Mar 2021 09:47:14 +1100 Subject: initial commit --- fizzbuzz.asm | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 fizzbuzz.asm (limited to 'fizzbuzz.asm') diff --git a/fizzbuzz.asm b/fizzbuzz.asm new file mode 100644 index 0000000..b1b5d14 --- /dev/null +++ b/fizzbuzz.asm @@ -0,0 +1,117 @@ +; Version : 2.11 +; Last update : 30/7/2020 +; Description : FizzBuzz, but in assembly. Left at a good tradeoff between speed and complexity. +; Licence : GPLv3 + +section .data +fizz db 'Fizz',0xA +fizzlen equ $-fizz +buzz db 'Buzz',0xA +buzzlen equ $-buzz +fizzbuzz db 'FizzBuzz',0xA +fizzbuzzlen equ $-fizzbuzz + +section .text +global _start +_start: +xor r12,r12 ;counter register, set to count 0 to 100 +xor r9,r9 ;r9 is used as a boolean to record if modulus 3 is achieved +jmp skip ;skip incrementing the first time round so 0 is kept + +increment: inc r12 ;increment the counter +skip: cmp r12,101 ;check if 101 has been reached +;skip: cmp r12,1000001 ;check if 101 has been reached +je exit ; Exit if numbers 0 to 100 have been calculated + +check3: imul r8d,r12d,0xaaaaaaab;calculate modulus 3 quickly by multiplying the counter by 0xaaaaaaab and truncuating the result into r8d. +cmp r8,0x55555555 +ja check5 ;if r8 is larger than 0x55555555; (r12 % 3 !=0) and so modulus 5 is directly jumped to. + +;falls through if (r12 % 3 == 0) +mov r9,1 ; set the fizz boolean to true + +check5: imul r8d,r12d,0xcccccccd ;calculate modulus 5 quickly by multiplying the counter by 0xcccccccd and truncuating the result into r8d. +cmp r8,0x33333333 +ja checkfizz ;if r8 is larger than 0x33333333; (r12 % 5 !=0). The state of fizz needs to be check to determine if anything needs printing. + +;; fall through if (r12 % 5 == 0) +test r9,r9 ;r9 is the fizz boolean +je buzzonly ;if there is no fizz, only print buzz + +;; fall through if both fizz and buzz are achieved; it's fizzbuzz time +printfizzbuzz: mov edi,1 ;file descriptor 1=stdout +mov rsi,fizzbuzz ;mov memory address of text to print into rsi. +mov rdx,fizzbuzzlen ;mov amount of bytes to write into rdx. +mov eax,1 ;1=sys_write syscall. +syscall ;do the syscall (sys_write in this case) + +xor r9,r9 ;zero out the fizz boolean +jmp increment + +checkfizz: test r9,r9 ;check if fizz is achieved +je printnum ;if not, print current number + +;; fall through if fizz is achieved +fizzonly: +mov edi,1 ; File descriptor 1=stdout +mov rsi,fizz ;mov memory address of text to print into rsi. +mov rdx,fizzlen ;mov amount of bytes to write into rdx. +mov eax,1 ;1=sys_write syscall. +syscall ;do the syscall (sys_write in this case) +xor r9,r9 ;zero out the fizz boolean +jmp increment + +buzzonly: +mov edi,1 ;file descriptor 1=stdout +mov rsi,buzz ;mov memory address of text to print into rsi. +mov rdx,buzzlen ;mov amount of bytes to write into rdx. +mov eax,1 ;1=sys_write syscall. 1 must be moved into rax after syscall is used, since rax is overwritten with the return value +syscall ;do the syscall (sys_write in this case) +xor r9,r9 ;zero out the fizz boolean +jmp increment + + +printnum: ALIGN 16 ;align the stack to 16 bytes. +mov ecx, 0xA ;base 10 +dec rsp +mov [rsp],cl ;newline = 0xa = base +mov rsi,rsp ;move the stack pointer to the source register +;sub rsp,16 ;the red-zone is big enough on 64 bit linux to allow printing ~8 or even more digits without modifying the stack pointer. Change the LEA below from "[rsp+16+1]" to "lea edx, [rsp+1]" if removed. + +;; rsi is pointing at '\n' on the stack, with 16B of "allocated" space below that. (if the stack pointer wasn't modified, the red-zone is written into instead) +mov rax,r12 ;rax = counter +mov r10,0xcccccccd ;used in division by 10 below + +;; ecx=remainder = low digit = 0..9. eax/=10 +toascii_digit: mov ecx,eax ;copy eax to ecx for later usage in modulus. + +imul rax,r10 ;sign multiply rax by 0xcccccccd to complete the first step of division by 10 +shr rax,0x23 ;shift rax right by 35 to finish off rax/=10 + +mov edx,eax ;move eax to edx so modulus can be done on it without clobbering eax +lea edx,[rdx+rdx*4] ;multiply edx by 5 as the first step of modulus +add edx,edx ;multiply edx by 2 as the second step of modulus +sub ecx,edx ;subtract edx from the original number (before /=10) to finish off modulus 10 + +;; the remainder(ecx) is the next calculated digit +add ecx,'0' ;add 48 to ecx to turn it into the ASCII representation of the number +dec rsi ;store digits in MSD-first printing order, working backwards from the end of the string +mov [rsi],cl ;shove cl(lower 8 bits in rcx) onto the stack + +test eax,eax +jnz toascii_digit ;if eax does not equal zero, there are more ASCII chars to generate and shove onto the stack + +;; rsi points to the first digit +mov eax,1 ;sys_write +mov edi,1 ;fd = STDOUT_FILENO +lea edx,[rsp+1] ;yes, it's safe to truncate pointers before subtracting to find length. +sub edx,esi ;calculate length, including the \n +syscall ;print the chars on the stack + +;add rsp, 24 ;undo the push and the buffer reservation (32-bit: add esp,20) +jmp increment + +exit: +mov eax, 60 ;code for sys_exit +xor edi, edi ;return value of 0 +syscall ;do sys_exit -- cgit v1.2.3