xor eax, eax
, but using mov eax, 0
due to more appropriate instruction semantics. I decided that since the program is purely for educational purposes, it is possible to unleash and pursue the style of the code in assembler. SECTION .text org 0x100 mov ah, 0x9 mov dx, hello int 0x21 mov ax, 0x4c00 int 0x21 SECTION .data hello: db "Hello, world!", 0xD, 0xA, '$'
%include "win32n.inc" extern MessageBoxA import MessageBoxA user32.dll extern ExitProcess import ExitProcess kernel32.dll SECTION code use32 class=code ..start: push UINT MB_OK push LPCTSTR window_title push LPCTSTR banner push HWND NULL call [MessageBoxA] push UINT NULL call [ExitProcess] SECTION data use32 class=data banner: db "Hello, world!", 0xD, 0xA, 0 window_title: db "Hello", 0
ret
instruction. For example, this subroutine in DOS displays the string “Hello, world” to the console: print_hello: mov ah, 0x9 mov dx, hello int 0x21 ret
call
instruction: call print_hello
printf
function from the C library is called like this: push hello call _printf add esp, 4
print_hello: push ebp ; mov ebp, esp ;
mov esp, ebp pop ebp ret
print_hello: push ebp mov ebp, esp sub esp, 8 ; 8 ,
print_hello: enter 8, 0 ; , 8 leave ; ret
enter
instruction is the nesting level of the subroutine. It is needed for linking with high-level languages ​​that support this method of organizing subroutines. In our case, this value can be left zero.main.asm
- the main filefunctions.asm
- routines,string_constants.asm
- definitions of string constants,Makefile
- build script %define SUCCESS 0 %define MIN_MAX_NUMBER 3 %define MAX_MAX_NUMBER 4294967294 global _main extern _printf extern _scanf extern _malloc extern _free SECTION .text _main: enter 0, 0 ; call input_max_number cmp edx, SUCCESS jne .custom_exit mov [max_number], eax ; mov eax, [max_number] call allocate_flags_memory cmp edx, SUCCESS jne .custom_exit mov [primes_pointer], eax ; mov eax, [primes_pointer] mov ebx, [max_number] call find_primes_with_eratosthenes_sieve ; mov eax, [primes_pointer] mov ebx, [max_number] call print_primes ; mov eax, [primes_pointer] call free_flags_memory ; .success: push str_exit_success call _printf jmp .return .custom_exit: push edx call _printf .return: mov eax, SUCCESS leave ret %include "functions.asm" SECTION .data max_number: dd 0 primes_pointer: dd 0 %include "string_constants.asm"
input_max_number
- using the console prompts the user for the maximum number to which the search for primes is performed; to avoid errors, the value is limited by the constants MIN_MAX_NUMBER
and MAX_MAX_NUMBER
allocate_flags_memory
— request the OS to allocate memory for an array of number marks (simple / compound) on the heap; if successful, returns a pointer to the allocated memory through the eax
registerfind_primes_with_eratosthenes_sieve
- filter out composite numbers using the classic sieve of Eratosthenes;print_primes
- output a list of prime numbers to the console;free_flags_memory
- free memory allocated for flagseax
register, the edx
contains the status. If successful, it contains the value of SUCCESS
, that is, 0
, in case of failure, the address of the string with an error message that will be displayed to the user.string_constants.asm
file contains the definition of string variables whose values, as the file name suggests, are not supposed to be changed. Only for the sake of these variables an exception was made to the rule “not to use global variables”. I never found a more convenient way to deliver string constants to I / O functions - I even thought of writing to the stack just before the function calls, but I decided that this idea was much worse than the idea with global variables. ; -, str_max_number_label: db "Max number (>=3): ", 0 str_max_number_input_format: db "%u", 0 str_max_number_output_format: db "Using max number %u", 0xD, 0xA, 0 str_print_primes_label: db "Primes:", 0xD, 0xA, 0 str_prime: db "%u", 0x9, 0 str_cr_lf: db 0xD, 0xA, 0 ; str_exit_success: db "Success!", 0xD, 0xA, 0 str_error_max_num_too_little: db "Max number is too little!", 0xD, 0xA, 0 str_error_max_num_too_big: db "Max number is too big!", 0xD, 0xA, 0 str_error_malloc_failed: db "Can't allocate memory!", 0xD, 0xA, 0
ifdef SystemRoot format = win32 rm = del ext = .exe else format = elf rm = rm -f ext = endif all: primes.o gcc primes.o -o primes$(ext) $(rm) primes.o primes.o: nasm -f $(format) main.asm -o primes.o
; ; : EAX - input_max_number: ; -, ;4 enter 4, 1 ; push str_max_number_label ;. string_constants.asm call _printf add esp, 4 ; scanf mov eax, ebp sub eax, 4 push eax push str_max_number_input_format ;. string_constants.asm call _scanf add esp, 8 mov eax, [ebp-4] ; cmp eax, MIN_MAX_NUMBER jb .number_too_little cmp eax, MAX_MAX_NUMBER ja .number_too_big jmp .success ; .number_too_little: mov edx, str_error_max_num_too_little ;. string_constants.asm jmp .return .number_too_big: mov edx, str_error_max_num_too_big ;. string_constants.asm jmp .return .success: push eax push str_max_number_output_format ;. string_constants.asm call _printf add esp, 4 pop eax mov edx, SUCCESS .return: leave ret
scanf
function from the C library: mov eax, ebp sub eax, 4 push eax push str_max_number_input_format ;. string_constants.asm call _scanf add esp, 8 mov eax, [ebp-4]
eax
records the memory address 4 bytes below the stack base pointer. This is the memory allocated for the local needs of the subroutine. A pointer to this memory is passed to the scanf
function as a target for writing data entered from the keyboard.eax
memory. ; ; : EAX - ; : EAX - allocate_flags_memory: enter 8, 1 ; EAX+1 inc eax mov [ebp-4], eax push eax call _malloc add esp, 4 ; cmp eax, 0 je .fail mov [ebp-8], eax ; mov byte [eax], 0 cld mov edi, eax inc edi mov edx, [ebp-4] add edx, eax mov al, 1 .write_true: stosb cmp edi, edx jb .write_true ; mov eax, [ebp-8] jmp .success .fail: mov edx, str_error_malloc_failed ;. string_constants.asm jmp .return .success: mov edx, SUCCESS .return: leave ret ; ; : EAX - free_flags_memory: enter 0, 1 push eax call _free add esp, 4 leave ret
malloc
and free
functions from the C library.malloc
returns the address of the allocated memory through the eax
register, in case of failure, this register contains 0
. This is the bottleneck of the program for the maximum number. 32 bits is quite enough to search for primes up to 4,294,967,295, but it will not be possible to allocate so much memory at once. ; ;: EAX - , EBX - find_primes_with_eratosthenes_sieve: enter 8, 1 mov [ebp-4], eax add eax, ebx inc eax mov [ebp-8], eax ; cld mov edx, 2 ;p = 2 mov ecx, 2 ; = 2 .strike_out_cycle: ;x = c*p mov eax, edx push edx mul ecx pop edx cmp eax, ebx jbe .strike_out_number jmp .increase_p .strike_out_number: mov edi, [ebp-4] add edi, eax mov byte [edi], 0 inc ecx ;c = c + 1 jmp .strike_out_cycle .increase_p: mov esi, [ebp-4] add esi, edx inc esi mov ecx, edx inc ecx .check_current_number: mov eax, ecx mul eax cmp eax, ebx ja .return lodsb inc ecx cmp al, 0 jne .new_p_found jmp .check_current_number .new_p_found: mov edx, ecx dec edx mov ecx, 2 jmp .strike_out_cycle .return: leave ret
; ; : EAX - , EBX - print_primes: enter 12, 1 mov [ebp-4], eax mov [ebp-8], ebx push str_print_primes_label call _printf add esp, 4 cld mov esi, [ebp-4] mov edx, esi add edx, [ebp-8] inc edx mov [ebp-12], edx mov ecx, 0 .print_cycle: lodsb cmp al, 0 jne .print jmp .check_finish .print: push esi push ecx push str_prime ;. string_constants.asm call _printf add esp, 4 pop ecx pop esi mov edx, [ebp-12] .check_finish: inc ecx cmp esi, edx jb .print_cycle push str_cr_lf call _printf add esp, 4 leave ret
printf
function from the C library./O2
performed a search up to the number 2 30 in about 25 seconds on my machine. The assembler program showed 15 seconds with the Sieve of Eratosthenes.Source: https://habr.com/ru/post/165397/
All Articles