Redaktor:Fabcde/pieskovisko

Zdvojnásobovanie upraviť

Zdvojnásobovanie je metóda (algoritmus) používaná k výpočtu súčinu dvoch činiteľov. Táto metóda umožňovala v dobe pred objavením základných aritmetických algoritmov nahradiť operáciu násobenia operáciami sčítavania.

Zdvojnásobovanie bolo v minulosti považované tiež za jednu zo základných matematických operácií.

Ukážka výpočtu upraviť

Pre nájdenie súčinu   rozložíme menší činiteľ   na súčet mocnín čísla  :

 

a opakovaným zdvojnásobovaním väčšieho činiteľa vypočítame jeho násobky mocnín čísla  :

2n 2n·639
0 1 639
1 2 1278
2 4 2556
3 8 5112
4 16 10224
5 32 20448
6 64 40896

Platí:

 

WikiKnihy

Assembler na platforme Windows 64-bit - x86-64

Úvod upraviť

Toto sú poznámky k niekoľkým jednoduchým programom napísaných v jazyku symbolických adries (assembly language) pre operačný systém Windows 64-bit na procesore x86-64. Programy sú písané pre prekladač (kompilátor) Netwide Assembler (NASM), alebo pre GNU Assembler (GAS).

Kvôli jednoduchšiemu rozlíšeniu majú zdrojové súbory programov pre kompilátor NASM príponu .asm, objektové .obj, pre GNU Assembler .s, resp .o.

Objektové súbory sú zlinkované do výsledného exe súboru pomocou linkeru golink, alebo ld. Kompilátor as (GNU Assembler) a linker ld je súčasťou gcc.

NASM používa syntax Intelu, dominujúcu v prostredí MS-DOS a Windows, GNU Asembler používa syntax AT&T, prevládajúcu v Unixovom svete. Jedným z rozdielov týchto dvoch syntaxí je aj opačné poradie argumentov v niektorých inštrukciách. Napríklad inštrukcia "vlož hodnotu nula do registra AX" sa v NASM zapisuje MOV AX, 0, v GAS MOV $0, %AX. Intelovská syntax (NASM) pripomína priraďovací príkaz vyšších programovacích jazykov AX=0, AT&T syntax (GAS) skôr niečo ako 0->AX.

Volacie konvencie upraviť

Programy bežiace v Reálnom móde (operačný systém MS-DOS) alebo v móde (režime) Virtual 8086 (operačný systém Windows) mohli využívať služby operačného systému MS-DOS (MS-DOS API). Tieto sa volali pomocou softvérového prerušenia inštrukciou INT, napríklad INT 21h. 64-bitové verzie OS Windows režim Virtual 8086 nepodporujú. Služby operačného systému je možné zabezpečiť jedine volaním funkcií Windows API (WinAPI). Podprogramu (funkcii) je potrebné väčšinou nejakým spôsobom odovzdať argumenty a opačným smerom zase výsledok. V princípe neexistujú žiadne obmedzenia, ako by si mali alebo nemali medzi sebou volaný a volajúci podprogram odovzdávať dáta. Je možné zvoliť akýkoľvek fungujúci spôsob, či už pomocou registrov, pamäti, zásobníka, len treba vedieť o každej jednej volanej funkcii/podprograme, kde očakáva argumenty a kam ukladá výsledok (volacia konvencia).

Toto sú niektoré (najbežnejšie) volacie konvencie (calling convention) vo svete Windows:

  • cdecl - C declaration, pochádza z jazyka C, parametre sú ukladané na zásobník postupne z prava do ľava (kvôli podpore premenlivého počtu argumentov), výsledok je uložený buď v registry EAX (integer) alebo ST0 (float), zásobník čistí volajúca funkcia
  • pascal - parametre sú ukladané na zásobník z ľava do prava, zásobník čistí volaná funkcia (napríklad inštrukciou RET n)
  • stdcall - štandard pre Win32 API, parametre sú ukladané na zásobník z prava do ľava (ako cdecl), ale zásobník čistí volaná funkcia (ako pascal)
  • Microsoft x64 - volania WinAPI v 64-bitových programoch pre MS Windows, prvé štyry parametre sú uložené v RCX/XMM0, RDX/XMM1, R8/XMM2, R9/XMM3 (integer/float), zvyšné v zásobníku z prava do ľava, výsledok je vrátený v registry RAX alebo XMM0, zásobník čistí volajúca funkcia

Hello, World! upraviť

Náš prvý NASM program vypíše v príkazovom riadku krátky text a skončí:

; HelloWorld.asm

; kompilacia:
;   nasm -f win64 HelloWorld.asm
; linkovanie:
;   golink /console /ni /entry main HelloWorld.obj kernel32.dll
; alternativne linkovanie:
;   ld -e main -s HelloWorld.obj -o HelloWorld.exe c:\windows\system32\kernel32.dll


global main

extern GetStdHandle
extern WriteFile
extern ExitProcess


        section .text use64            ; Program code
main:
        ; rax = GetStdHandle(-11)
        ; HANDLE hStdHandle = WINAPI GetStdHandle (_In_ DWORD nStdHandle)
        ; nStdHandle: STD_INPUT_HANDLE=-10 , STD_OUTPUT_HANDLE=-11 , STD_ERROR_HANDLE=-12
        mov ecx, dword -11             ; 1. param _In_ DWORD nStdHandle
        call GetStdHandle

        ; rax = WriteFile(%rax, $message, $MESSAGE_LEN, %rsp-4, 0)
        ; BOOL bErrorFlag = WINAPI WriteFile (_In_ HANDLE hFile, _In_ LPCVOID lpBuffer, _In_ DWORD nNumberOfBytesToWrite, _Out_opt_ LPDWORD lpNumberOfBytesWritten, _Inout_opt_ LPOVERLAPPED lpOverlapped)
        ; WriteConsole(handle, &msg[0], 13, &written, 0)
        mov rcx, rax                   ; 1. param _In_ HANDLE hFile
        mov rdx, qword message         ; 2. param _In_ LPCVOID lpBuffer
        mov r8d, dword MESSAGE_LEN     ; 3. param _In_ DWORD nNumberOfBytesToWrite
        mov r9, lpNumberOfBytesWritten ; 4. param _Out_opt_ LPDWORD lpNumberOfBytesWritten
        push qword 0                   ; 5. param _Inout_opt_ LPOVERLAPPED lpOverlapped
        call WriteFile

        ; ExitProcess(0)
        ; VOID WINAPI ExitProcess( _In_ UINT uExitCode)
        xor ecx, ecx                   ; UINT je 32 bit aj v 64 bitovom prostredi
        call ExitProcess

message:                 db      "Hello, World!",0xd,0xa
MESSAGE_LEN:             equ     $-message


        section .bss use64             ; neinicializovana datova oblast

lpNumberOfBytesWritten:  resd      1

Program síce nealokuje miesto v zásobníku, ako to vyžaduje volacia konvencia Microsoft x64 (podrobnosti v ďalšom texte), napriek tomu sa dal zostaviť aj spustiť: kompilácia:

G:\>nasm -f win64 HelloWorld.asm

linkovanie:

G:\>golink /console /ni /entry main HelloWorld.obj kernel32.dll

alebo:

G:\>ld -e main -s HelloWorld.obj -o HelloWorld.exe c:\windows\system32\kernel32.dll

Ak kompilácia a linkovanie prebehlo úspešne, môžme vyskúšať náš prvý 64-bitový program:

G:\>dir
po 10.07.2017  12:55             2 174 HelloWorld.asm
po 10.07.2017  12:56             1 536 HelloWorld.exe
po 10.07.2017  12:56               418 HelloWorld.obj

G:\>HelloWorld.exe
Hello, World!

Direktíva global main deklaruje návestie main ako globálne a linker ho tak bude môcť použiť ako štartovaciu adresu programu.

Direktíva extern GetStdHandle deklaruje symbol GetStdHandle ako externý, čiže nachádzajúci sa v niektorom z ďalších pripojených súboroch, v tomto prípade v dynamicky linkovanej knižnici kernel32.dll.

Direktíva section .text use64 uvádza nasledujúci segment ako programový (readonly).

Inštrukcie

mov ecx, dword -11

call GetStdHandle

naplnia register ECX hodnotou -11 (STD_OUTPUT_HANDLE) a zavolajú WinAPI funkciu GetStdHandle.

Funkcia vracia v registry RAX (v súlade s volacou konvenciou) handle zariadenia STDOUT.

Inštrukcie

mov rcx, rax                   ; 1. param _In_ HANDLE hFile

mov rdx, qword message         ; 2. param _In_ LPCVOID lpBuffer

mov r8d, dword MESSAGE_LEN     ; 3. param _In_ DWORD nNumberOfBytesToWrite

mov r9, lpNumberOfBytesWritten ; 4. param _Out_opt_ LPDWORD lpNumberOfBytesWritten

push qword 0                   ; 5. param _Inout_opt_ LPOVERLAPPED lpOverlapped

call WriteFile

vložia prvé štyri argumenty funkcie WriteFile do príslušných registrov, piaty do zásobníka a zavolajú ju.

Nakoniec

xor ecx, ecx

call ExitProcess

vynuluje obsah registra ECX a ukončí program. Jediným argumentom funkcie ExitProcess (uložený v registry ECX) je exit code programu.

Nasleduje aj verzia pre GNU Assembler:

# HelloWorld.s

# kompilacia:
#   as HelloWorld.s -o HelloWorld.o
# linkovanie:
#   ld -e main -s HelloWorld.o -o HelloWorld.exe c:\windows\system32\kernel32.dll
# alternativna kompilacia+linkovanie:
#   gcc -m64 -nostartfiles -Wl,-emain -o HelloWorld.exe HelloWorld.s c:\windows\system32\kernel32.dll

.section .text

.global main

main:
        /* rax = GetStdHandle(-11) */
        /* HANDLE hStdHandle = WINAPI GetStdHandle (_In_ DWORD nStdHandle) */
        /* nStdHandle: STD_INPUT_HANDLE=-10 , STD_OUTPUT_HANDLE=-11 , STD_ERROR_HANDLE=-12 */
        mov $-11, %ecx            # 1. param _In_ DWORD nStdHandle
        call GetStdHandle

        /* rax = WriteFile(%rax, $message, $MESSAGE_LEN, %rsp-4, 0) */
        /* BOOL bErrorFlag = WINAPI WriteFile (_In_ HANDLE hFile, _In_ LPCVOID lpBuffer, _In_ DWORD nNumberOfBytesToWrite, _Out_opt_ LPDWORD lpNumberOfBytesWritten, _Inout_opt_ LPOVERLAPPED lpOverlapped) */
        /* WriteConsole(handle, &msg[0], 13, &written, 0) */
        mov %rax, %rcx            # 1. param _In_ HANDLE hFile
        mov $message, %rdx        # 2. param _In_ LPCVOID lpBuffer
        mov $MESSAGE_LEN, %r8d    # 3. param _In_ DWORD nNumberOfBytesToWrite
        sub $4, %rsp              # rezervovanie miesta (4 bajty) v zasobniku pre 4. param
        mov %rsp, %r9             # 4. param _Out_opt_ LPDWORD lpNumberOfBytesWritten
        pushq $0                  # 5. param _Inout_opt_ LPOVERLAPPED lpOverlapped
        call WriteFile

        /* ExitProcess(0) */
        /* VOID WINAPI ExitProcess( _In_ UINT uExitCode) */
        xor %ecx, %ecx            # 1. param _In_ UINT uExitCode UINT je 32 bit aj v 64 bitovom prostredi
        call ExitProcess

message:
        .ascii  "Hello, World!\n"
MESSAGE_LEN = . - message

kompilacia:

G:\>as HelloWorld.s -o HelloWorld.o

linkovanie

G:\>ld -e main -s HelloWorld.o -o HelloWorld.exe c:\windows\system32\kernel32.dll

alebo

G:\>gcc -m64 -nostartfiles -Wl,-emain -o HelloWorld.exe HelloWorld.s c:\windows\system32\kernel32.dll

Pozn: golink si nerozumel s objektovým súborom kompilátora as

Referencie upraviť

[[Kategória:Algoritmy]]