Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Низкоуровневое программирование > Assembler - Ассемблер (FASM, MASM, WASM, NASM, GoASM, Gas, RosAsm, HLA) и не рекомендуем TASM
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 03.12.2014, 11:40   #31
Mikl___
Участник клуба
 
Регистрация: 11.01.2010
Сообщений: 1,162
По умолчанию

Сортирую двойные слова
Пузырьковая
Код:
.data
R dd n-1;количество неотсортированных элементов минус один
array  dd 10,450,320,120,180,600,50,230,340,460,550,500,130
      dd 80,390,410,20,800,670,60,730,610,310,0,360,200
n = ($-array)/4
.code
    mov esi,offset array    ;позиционируемся на массив
a2:    mov ecx,R    
    xor ebx,ebx        ;флаг – были/не были перестановки в проходе
a3:    mov eax,[esi+ecx*4-4]    ;получаем значение очередного элемента    
    cmp [esi+ecx*4],eax    ;сравниваем со значением соседнего элемента
    jnb a4    ;если больше или равен - идем к следующему элементу
    setna bl    ;была перестановка - взводим флаг
    xchg eax,[esi+ecx*4]    ;меняем значение элементов местами
    mov [esi+ecx*4-4],eax
a4:    loop a3    ;двигаемся вверх до границы массива
    add esi,4    ;сдвигаем границу отсортированного массива
    dec ebx    ;проверяем были ли перестановки
    jnz exit    ;если перестановок не было - заканчиваем сортировку
    dec R        ;уменьшаем количество неотсортированных элементов
    jnz a2;если есть еще неотсортированные элементы - начинаем новый проход
exit:
При упорядочении массива из n элементов произойдет в лучшем случае, если массив отсортирован — n-1 сравнений. В худшем случае, если массив отсортирован в обратном порядке — при сортировке произойдет n*(n-1)/2 сравнений. (для n=26 элементов лучший случай — 25, средний — 289, худший — 325)
Шейкер
Код:
.data
H dd 0    ;верхняя граница неотсортированного массива
L dd n-1    ;нижняя граница неотсортированного массива
.code
    xor esi,esi        ;нижняя граница неотсортированного массива
    xor ebx,ebx        ;флаг - были/не были перестановки в проходе
    mov ecx,L;количество неотсортированных элементов снизу минус один
a4:    inc esi
      call Compare_and_Swapping    ;сравнение и обмен значений элементов
    loop a4                ;двигаемся вниз до границы массива
    dec ebx                ;проверяем были ли перестановки
    jnz exit        ;если перестановок не было - сортировка закончена 
    dec L        ;уменьшаем количество неотсортированных элементов снизу
    jz exit    ;достигли границы массива
    dec esi        ;esi=L
    mov ecx,esi
    sub ecx,H;количество неотсортированных элементов сверху минус один
    jecxz exit    ;если граница снизу равна границе сверху - выходим
a2:    call Compare_Swapping        ;сравнение и обмен значений элементов
    dec esi
    loop a2                ;двигаемся вверх до границы массива
    dec ebx                ;проверяем были ли перестановки
    jnz exit    ;если перестановок не было - заканчиваем сортировку
    inc H    ;уменьшаем количество неотсортированных элементов сверху
    inc esi        ;esi=H
    mov ecx,L    
    sub ecx,esi    ;если граница снизу больше, чем граница сверху – значит 
    ja a4;есть еще неотсортированные элементы - начинаем новый проход
exit:    . . .
Compare_Swapping proc;сравнение и обмен значений соседних элементов
    mov eax,array[esi*4]    ;получаем значение очередного элемента
    cmp array[esi*4-4],eax    ;сравниваем его с соседним элементом
    jna a3    ;если меньше или равен - идем к следующему элементу
    seta bl    ;если была перестановка - взводим флаг
    xchg eax,array[esi*4-4]        ;меняем значения элементов местами
    mov array[esi*4],eax
a3:    ret
Compare_Swapping endp
n=26 элементов лучший случай — 25, средний — 259 (лучше пузырьковой сортировки на 20%), худший — 325
Mikl___ вне форума Ответить с цитированием
Старый 03.12.2014, 11:44   #32
Mikl___
Участник клуба
 
Регистрация: 11.01.2010
Сообщений: 1,162
По умолчанию

Пирамидальная
Код:
.data
L dd n/2-1    ;левая граница неотсортированного массива
R dd n-1        ;правая граница неотсортированного массива
.code
;массив преобразуется в отображение пирамиды – вызвать процедуру
;down_heap n/2 раз для преобразования массива  в пирамиду
b0:     call down_heap
        dec L        ;while ( L > 0 ) L--;
        jnz short b0
;собственно пирамидальная сортировка
       dec L        ;L=0
       dec R        ;R=n-2
b1:     mov edx,R   ;отправляем значение максимального
       mov eax,array    ;элемента в конец массива
        xchg eax,array[edx*4+4] ; array[0] <--> array[R];
        mov array,eax
        call down_heap   ;восстанавливаем пирамиду - на ее 
;вершине появляется новое максимальное значение      
        dec R        ;уменьшаем индекс последнего элемента
        jnz short b1    ;while ( R > 0 ) R--;
b2:        ...
;-----------------------------------------------------
down_heap proc; процедура вставки элемента на свое место в пирамиду
        mov eax,L
        mov ebx,eax            ;i = L;
        shl eax,1            ;j = 2*L;
        mov esi,array[eax*2]    ;item = array[L]; 
        cmp eax,R     ;if( j<R && array[j] < array[j+1]) j++; 
        jnb short a0
        mov edx,array[eax*4]
        cmp edx,array[eax*4+4]    ;array[j] < array[j+1] ?
        jnb short a0
; условие j<R && array[j]<array[j+1] выполнилось
        inc eax              ;j++
a0:     cmp eax,R           ;while( j<=R && item < array[j])
        ja short a1
        mov edi,array[eax*4]
        cmp esi,edi        ;item < array[j] ?
        jnb short a1
; условие j<=R && item < array[j] выполнилось
       mov array[ebx*4],edi    ;array[i] = array[j];
        mov ebx,eax        ;i = j;                                    
        shl eax,1            ;j = 2*j;
        cmp eax,R
        jnb short a0;if( j<R && array[j] < array[j+1]) j++;
        mov edx,array[eax*4]
        cmp edx,array[eax*4+4]
        jnb short a0
; условие j<R && array[j] < array[j+1] выполнилось
        inc eax            ;j++
       jmp short a0
a1:       mov array[ebx*4],esi    ;array[i] = item;
        retn
down_heap endp
n=26 элементов средний случай— 109 (лучше пузырьковой сортировки почти в 3 раза)
сортировка прямым включением
Код:
mov esi,4       ;i=1
a4:    push esi
a3:    mov eax,array[esi-4]
     cmp eax,array[esi]
    jb a2
    xchg eax,array[esi]
    sub esi,4    ;двигаемся к началу массива
    mov array[esi],eax
    jnz a3
a2:    pop esi
     add esi,4       ;двигаемся к концу массива
    cmp esi,n*4     ;просмотрели весь массив?
    jb a4
n=26 элементов лучший случай — 25, средний — 172 (лучше пузырьковой сортировки на 40%), худший — 325
Алгоритм можно улучшить пользуясь тем, что готовая последовательность уже упорядочена. Место вставки нового элемента можно найти значительно быстрее, если применить бинарный поиск, исследовав сперва средний элемент упорядоченной последовательности и продолжая деление пополам, пока не будет найдено место вставки. Для n=26 элементов лучший случай — 25, средний и худший — 106 (лучше пузырьковой сортировки почти в 3 раза)
Код:
mov ebx,1  ;ebx - граница неотсортированного массива
b1:    mov edi,ebx
    mov edx,edi;edi индекс первого элемента отсортированного массива
    xor esi,esi;esi индекс последнего элемента отсортированного массива
    mov eax,array[ebx*4]
    cmp eax,array[ebx*4-4]
    jnb b2
b6:    cmp esi,edi        ;проверка esi>edi на завершение поиска
    jg b5    ;проверены все элементы, вставляем новый элемент 
    shr edx,1 ;индекс центрального элемента равен (edi+esi)/2
    cmp array[edx*4],eax;сравниваем с искомым значением
    ja b3               ;array[edx*4]<eax
    jz b5               ;array[edx*4]=eax
     inc edx        ;учтем только что проверенное значение
    mov esi,edx    ;изменяем нижнюю границу поиска
    add edx,edi    ;создаем индекс центрального элемента
    jmp short b6    ;переходим к следующему элементу
b3:    dec edx        ;учтем только что проверенное значение
    mov edi,edx    ;изменяем верхнюю границу поиска
    add edx,esi    ;создаем индекс центрального элемента
    jmp b6        ;переходим к следующему элементу
b5:    mov ecx,ebx    ;сдвигаем отсортированные элементы, чтобы
    sub ecx,esi    ;освободить место для нового элемента
    shl esi,2; esi=esi*4
    push eax
b7:    mov eax,array[esi+ecx*4-4];сдвиг отсортированных элементов
    mov array[esi+ecx*4],eax
    loop b7
    pop eax
    mov array[esi],eax;вставляем новый элемент
b2:  inc ebx
    cmp ebx,n
    jb b1
Mikl___ вне форума Ответить с цитированием
Старый 03.12.2014, 11:50   #33
Mikl___
Участник клуба
 
Регистрация: 11.01.2010
Сообщений: 1,162
По умолчанию

сортировка прямым выбором
На массиве из n элементов время выполнения в худшем, среднем и лучшем случае n*(n-1)/2
Код:
mov edi,offset array    ;edi = указатель на массив
       mov ecx,N            ;ecx = количество элементов
a0:    lea ebx,[edi+ecx*4]    ;ebx = максимальный индекс в проходе+1
     mov eax,[edi]    ;eax=min=величина первого элемента в проходе
a1:     sub ebx,4        ;двигаемся по проходу вверх
     cmp eax,[ebx]
     jbe a2        ;min > array[ebx] ?
     xchg eax,[ebx]    ;swap(array[ebx],min)
a2:     cmp ebx,edi
     jnz a1        ;проход закончился?
     stosd ;mov [edi],eax edi=+4 на первой позиции минимальный элемент
     loop a0
сортировка Шелла
Среднее время работы алгоритма зависит от длин промежутков, на которых будут находится сортируемые элементы исходного списка на каждом шаге алгоритма
при выборе последовательности значений d1=n/2, d2=d1/2,...,1 в худшем случае алгоритм выполнит O(n2) — сравнений 140
Код:
Table dd 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1
все значения (3^j−1)/2 < n, такая последовательность шагов приводит к алгоритму класса O(n^(3/2)) — сравнений 108
Код:
Table dd 797161, 265720, 88573, 29524, 9841, 3280, 1093, 364, 121, 40, 13, 4, 1
последовательности вида N=2*N+1 — сравнений 118
Код:
Table dd 32767, 16383, 8191, 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1
последовательность Дж.Инсерпи и Р.Седгевика — сравнений 115:
Код:
Table dd 198768, 86961, 33936, 13776, 4592, 1968, 861, 336, 112, 48, 21, 7, 3, 1
Код:
.data
Table dd 797161,265720,88573,29524,9841,3280,1093,364,121,40,13,4,1
.code
mov ecx,-14;в таблице приращений 13 элементов
entry: inc ecx
jz exit; если последний элемент в таблице - выход из программы
cmp [Table+ecx*4+13*4],n; ищем максимальное приращение (gap), 
jge entry;соответствующее размеру нашего массива
a6:    mov edx,[Table+ecx*4+13*4];получаем очередное приращение из таблицы
shl edx,2;выбрали интервал,у нас двойные слова,поэтому edx=edx*4
a2:    mov ebx,edx;i=gap
a3:    mov esi,ebx
sub esi,edx;j=i-gap
a4:    mov eax,array[esi];for(i=gap;i<dim;i++)/*проход массива*/
    cmp eax,array[esi+edx];сравнение пар,отстоящих 
jbe a5;на gap друг от друга
xchg eax,array[esi+edx];swap(a[j],a[j+gap])
mov array[esi],eax
sub esi,edx  ;j-=gap
jge a4 
a5:    add ebx,4    ;i++
cmp ebx,n*4  ;i < dim
jb a3        ;for(j=i-gap; j>=0 && a[j] > a[j+gap]
inc ecx
jnz a6
exit:
сортировка Хоара (быстрая сортировка)
Сортировка даёт в среднем O(n log n) сравнений
Код:
push    offset array+n*4-4;указатель на последний элемент
        push    offset array;указатель на первый элемент массива
        call    quick_sort      ; quicksort (data, data+n-1)
;-----------------------------------------------------
partition proc    Lb:dword,    Ub:dword
; функция partition возвращает адрес pivot 
        mov edx,Ub
       sub edx,eax             ;eax=Lb
       shr edx,3             ;(Ub-Lb)/2
; После завершения этого цикла все значения слева от элемента pivot 
; будут меньше значения элемента pivot, а все значения справа от 
; элемента pivot будут больше, чем значение элемента pivot
        mov edi,[eax+edx*4]   ;получаем указатель на pivot
;pivot = *(Lb+(Ub-Lb)/2)
        cmp eax,Ub         ;eax=Lb
        ja short b0             ;return Lb;
; Поиск значения, большего чем pivot, в нижней части массива
b1:     mov eax,Lb        
        cmp [eax],edi        ;edi=pivot
        jnl short b3        ;while (*Lb < pivot)
       add Lb,4            ;Lb++;   
       jmp short b1
; Поиск значения, меньшего чем pivot, в верхней части массива
b4:     sub Ub,4        
b3:     mov eax,Ub
        cmp [eax],edi        ;while (*Ub > pivot)
        jg short b4             ;Ub-- 
        mov ecx,Lb
       cmp ecx,eax             ;eax=Ub
        ja short b5           ;if(Lb <= Ub)
        sub Ub,4        ;swap( Lb++, Ub-- )
        add Lb,4        
        mov edx,[eax]        ;eax=Ub
        xchg edx,[ecx]        ;ecx=Lb
        mov [eax],edx        ;*Ub<-->*Lb;
b5:     mov eax,Lb
        cmp eax,Ub
        jbe short b1        ;while (*Lb < pivot)
b0:     pop ebp
        retn 8
partition endp
;------------------------------------------------------------
quick_sort proc Lb:dword, Ub:dword
         mov eax,Lb
         cmp eax,Ub ; if (Lb >= Ub)
         jnb short exit1    ;сортировать нечего
         push Ub
         push eax        ;eax=Lb
         call partition  
        mov edi,eax     ;eax=pivot_ptr
         sub eax,4        ;pivot_ptr-1
         push eax
         push Lb    
         call quick_sort ;отсортируем то, что слева от pivot
         push Ub
         push edi        ;edi=pivot_ptr
         call quick_sort ; и то, что справа от pivot
exit1:   pop ebp
         retn 8
quick_sort endp
Mikl___ вне форума Ответить с цитированием
Старый 03.12.2014, 12:10   #34
Mikl___
Участник клуба
 
Регистрация: 11.01.2010
Сообщений: 1,162
По умолчанию

Блокируем клавиатуру в Windows
Для блокировки клавиатуры существует несколько путей
  1. функцией BlockInput
  2. блокировкой немаскированных прерываний командой CLI
  3. посылкой в контроллер клавиатуры команды 0xAD
  4. отправим контроллеру клавиатуры адрес, а данные забываем
  5. через реестр
  1. в user32.dll есть функция BlockInput, ниже программа которая отключает клавиатуру на 5 секунд
    Код:
    ; masm windows gui #
    .686
    .model flat
    include windows.inc           
    includelib \masm32\lib\kernel32.lib
    includelib \masm32\lib\user32.lib
    extern _imp__BlockInput@4:dword
    extern _imp__Sleep@4:dword
    .code
    start:  push TRUE
        call _imp__BlockInput@4; отключить клаву
        push 5000
        call _imp__Sleep@4;выждать 5 секунд
        push FALSE
        call _imp__BlockInput@4; включить
        ret
    end start
    BlockInput блокирует мышь и почти всю клавиатуру, но имеет недостаток: все это разблокируется при нажатии Ctrl-Alt-Del, внезапном завершении вызвавшей программы, или при критической системной ошибке.
  2. драйвер, который блокирует немаскированные прерывания (главным образом клавиатуру) на 5 секунд. Идем сюда, копируем текст драйвера scp00.sys, выкидываем из него всё ненужное пока не получится
    Код:
    ; masm windows native #
    ;написано на основе драйвера режима ядра beeper из «KmdTutor by Four-F»
    .686P
    .model flat
    include ntstatus.inc
    include ntddk.inc
    includelib hal.lib
    extern _imp__KeStallExecutionProcessor@4:dword
    .code
    DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING
        cli
        mov ecx,1000; 50 мкСек * 1000 = 5 Сек
    @@: push ecx
        push 50; максимальное количество мкСек для функции KeStallExecutionProcessor
        call _imp__KeStallExecutionProcessor@4
        pop ecx
        loop @b    
        sti
        mov eax, STATUS_DEVICE_CONFIGURATION_ERROR;возвращаем код ошибки, для 
        ret     ;того, чтобы система удалила драйвер из памяти
    DriverEntry endp
    запускаем драйвер одним из трех описанных способов
  3. команда 0хAD блокирует клавиатуру до момента разблокировки командой 0хАЕ
    Код:
    ; masm windows native #
    .686P
    .model flat
    include ntstatus.inc
    include ntddk.inc
    includelib hal.lib
    extern _imp__KeStallExecutionProcessor@4:dword
    .code
    DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING
        mov al,0ADh
        out 64h,al; блокируем
        mov ecx,1000; 50 мкСек * 1000 = 5 Сек
    @@: push ecx
        push 50; максимальное количество мкСек для функции KeStallExecutionProcessor
        call _imp__KeStallExecutionProcessor@4
        pop ecx
        loop @b    
        mov al,0AEh
        out 64h,al;снимаем блокировку
        mov eax, STATUS_DEVICE_CONFIGURATION_ERROR;возвращаем код ошибки, для 
        ret     ;того, чтобы система удалила драйвер из памяти
    DriverEntry endp
    действуем аналогично второму способу
  4. контроллер клавиатуры использует один и тот же порт, как для задания адреса, так и для чтения и записи данных. После передачи кода команды контроллер клавиатуры не будет обрабатывать нажатие клавиш, пока не будет передан байт данных
    Код:
    ; masm windows native #
    .686P
    .model flat
    include ntstatus.inc
    include ntddk.inc
    includelib hal.lib
    extern _imp__KeStallExecutionProcessor@4:dword
    .code
    DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING
        mov al,0F3h
        out 60h,al; блокируем
        mov ecx,1000; 50 мкСек * 1000 = 5 Сек
    @@: push ecx
        push 50; максимальное количество мкСек для функции KeStallExecutionProcessor
        call _imp__KeStallExecutionProcessor@4
        pop ecx
        loop @b    
        mov al,0F4h
        out 60h,al;снимаем блокировку
        mov eax, STATUS_DEVICE_CONFIGURATION_ERROR;возвращаем код ошибки, для 
        ret     ;того, чтобы система удалила драйвер из памяти
    DriverEntry endp
    аналогично способу два
Mikl___ вне форума Ответить с цитированием
Старый 03.12.2014, 12:20   #35
Mikl___
Участник клуба
 
Регистрация: 11.01.2010
Сообщений: 1,162
По умолчанию

Кто и зачем до сих пор использует DOS.
Какой смысл сейчас писать что-либо под эту ОС?
  • Под DOS программировать во многом проще. Структура работы логична и понятна, исходники открыты. Для новичков – DOS лучший полигон для работы.
  • По привычке. Многие начинали программировать, когда еще не было Windows 98/95/NT/XP и прочего.
  • Маленький размер и быстрая работа откомпилированных программ. Маленький размер исходников.
  • Возможность пощупать код на низком уровне, дабы лучше разобраться в работе программы, а не строить её из «кубиков», как часто это делается в Delphi\Buider, после чего непонятно с чем связана та или иная ошибка
  • Пока еще не все машины имеют на борту Windows\Linux, поэтому не стоит хоронить DOS
Mikl___ вне форума Ответить с цитированием
Старый 03.12.2014, 13:41   #36
Mikl___
Участник клуба
 
Регистрация: 11.01.2010
Сообщений: 1,162
По умолчанию

Reverse Engeneering

Reverse Engeneering в умелых руках и с хорошим инструментарием вообще способен творить чудеса. То, что на асме выглядело так:
Код:
RegMewMDIStatClass:
	add	esp, 0FFFFFFD8h
	mov	dword ptr [esp], 83h	; 'a'
	mov	dword ptr [esp+4], offset MDIStatwndproc
	xor	eax, eax
	mov	[esp+8], eax
	mov	dword ptr [esp+0Ch], 4
	mov	eax, ds:MewHINST
	mov	[esp+10h], eax
	xor	eax, eax
	mov	[esp+14h], eax
	push	7F00h
	push	0
	call	LoadCursorA
	mov	[esp+18h], eax
	xor	eax, eax
	mov	[esp+1Ch], eax
	xor	eax, eax
	mov	[esp+20h], eax
	mov	eax, offset aMewmdistat; "MewMDIStat"
	mov	[esp+24h], eax
	push	esp
	call	RegisterClassA
	add	esp, 28h
	retn
Сначала быстро и непринужденно превращается в более читабельное:
Код:
; int __pascal RegMewMDIStatClass()
RegMewMDIStatClass	proc near

	wc	= WNDCLASS ptr -28h

	add	esp, -28h
	mov	[esp+28h+wc.style], CS_VREDRAW or CS_HREDRAW or CS_PARENTDC
	mov	[esp+28h+wc.lpfnWndProc], offset MDIStatwndproc
	xor	eax, eax
	mov	[esp+28h+wc.cbClsExtra], eax
	mov	[esp+28h+wc.cbWndExtra], 4
	mov	eax, ds:MewHINST
	mov	[esp+28h+wc.hInstance], eax
	xor	eax, eax
	mov	[esp+28h+wc.hIcon], eax
	push	IDC_ARROW		; lpCursorName
	push	0			; hInstance
	call	LoadCursorA
	mov	[esp+28h+wc.hCursor], eax
	xor	eax, eax
	mov	[esp+28h+wc.hbrBackground], eax
	xor	eax, eax
	mov	[esp+28h+wc.lpszMenuName], eax
	mov	eax, offset aMewmdistat	; "MewMDIStat"
	mov	[esp+28h+wc.lpszClassName], eax
	push	esp			; lpWndClass
	call	RegisterClassA
	add	esp, 28h
	retn
	RegMewMDIStatClass	endp

; ------------
	align	4
	aMewmdistat	db 'MewMDIStat',0; DATA XREF: RegMewMDIStatClass+4B**
	align	10h
а затем уже просто в конфетку:
Код:
//  Detected compiler: Delphi

#include <windows.h>
#include <defs.h>

int __pascal RegMewMDIStatClass()
{
  int result; // eax@1
  WNDCLASS wc; // [sp+0h] [bp-28h]@1

  wc.style = CS_PARENTDC|CS_HREDRAW|CS_VREDRAW;
  wc.lpfnWndProc = (WNDPROC)MDIStatwndproc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 4;
  wc.hInstance = MewHINST;
  wc.hIcon = 0;
  wc.hCursor = LoadCursorA(0, (LPCSTR)IDC_ARROW);
  wc.hbrBackground = 0;
  wc.lpszMenuName = 0;
  wc.lpszClassName = "MewMDIStat";
  LOWORD(result) = RegisterClassA(&wc);
  return result;
};
И это при том, что изначально код был писан на Дельфи.
Mikl___ вне форума Ответить с цитированием
Старый 03.12.2014, 13:59   #37
Mikl___
Участник клуба
 
Регистрация: 11.01.2010
Сообщений: 1,162
По умолчанию

Ассемблерные вставки в Borland\Turbo Pascal
  1. Встроенный ассемблер реализует
    • команды для i8086 по умолчанию
    • команды i286 при наличии директивы {$G+}
    • команды сопроцессора при наличии директив {$G+} {$N+} {$E+}
    • 32-разрядные команды не поддерживаются
  2. для всех переменных, меток и комментариев используется синтаксис языка Pascal
  3. к встроенному ассемблеру обращаются через оператор
    Код:
    ASM
     команды ассемблера
    END;
  4. встроенный ассемблер не может (не должен) изменять содержимое регистров BP, SP, SS и DS
  5. встроенный ассемблер поддерживает только директивы DB, DW, DD
  6. все выражения вычисляются как 32-разрядные целые от -2147483648 до 4294967295
  7. значения выражений в вещественой форме не поддерживается
  8. Pascal воспринимает числа только в десятичной системе счисления или в шестнадцатеричном виде (префикс $)
  9. строковые константы в стиле языка Паскаль
  10. интерпретация параметра var как 32-разрядного указателя es:[регистр]. Требуется сначала параметр var командой les загрузить в регистр[asm]les регистр,var-параметр[/asm]а потом использовать как es:[регистр]
  11. в паскале есть директива assembler, которая сравнима с директивой external. Эта директива позволяет строить на Паскале ассемблерные процедуры по тем же правилам, что и внешние процедуры и функции
Например, требуется реализовать функцию Sum=x+y, переменные Sum, x и y определены как integer используем специальную переменную Паскаля @Result для возврата из функции значений
  1. на "чистом" паскале
    Код:
    var 
    x,y,s : integer
    Function Sum (x,y:integer):integer;
    Begin
     Sum := x+y;
    End;
  2. передача параметров по значению
    Код:
    Function Sum (x,y:integer):integer;
    begin
     asm
       mov ax,x
       add ax,y
       mov @Result,ax
     end;
    end;
  3. передача параметров по ссылке
    Код:
    Function Sum (var x,y:integer):integer;
    begin
     asm
      les di,x
      les bx,y
      mov ax,es:[di]  
      add ax,es:[bx]
       mov @Result,ax
     end;
    end;
  4. ассемблерная функция
Код:
Function Sum (x,y:integer):integer; assembler;
asm
  mov ax,x
  add ax,y
end;
результат
Код:
begin
 writeln('Вычислить: Sum = x + y для данных типа integer');
 write('Введите значение x');
 read(x);
 write('Введите значение y');
 read(y);
 s:=Sum(x,y);
 writeln(' ',x, ' + ',y,' = ',s);
Ассемблерные вставки в Borland С++
Код:
int FuncAsmI (int x)// эквивалент С-функции int FuncCI (int x) { return x/3;}
{ asm
   { mov ax,x
      cwd
      mov cx,3
      idiv cx
    }
}
unsigned int funcAsmU (unsigned int x)// эквивалент С-функции int FuncCu (unsigned int x) { return x/3;}
{ asm
   { mov ax,x
      xor dx,dx
      mov cx,3
      div cx
   }
}
Ассемблерные вставки в Microsoft Visual С++
  1. учитывайте, что имеете дело с 32-разрядной системой int — 32-разряда, short int — 16-разрядов
  2. оператор asm должен быть с двумя поддчерками __asm
Код:
short int FuncAsmI (short int x)
{ __asm
   { mov ax,x
      cwd
      mov cx,3
      idiv cx
    }
}
unsigned short int funcAsmU (unsigned short int x)
{ __asm
   { mov ax,x
      xor dx,dx
      mov cx,3
      div cx
   }
}
пример ассемблерной вставки, складывающей два числа
Код:
main()// обычный код на C++
{      int a = 1; // объявляем переменную a и кладем туда значение 1
        int b = 2; // объявляем переменную a и кладем туда значение 2
        int c;     // объявляем переменную c, но не инициализируем ее
        // а тут ассемблерная вставка
        __asm{
                mov eax, a   // загружаем значение переменной a в регистр EAX
                add eax, b // складываем a с b, записывая результат в EAX
                mov c, eax   // загружаем значение EAX в переменную c
        } // конец ассемблерной вставки
        // а потом снова обычный код на C++
        // выводим содержимое c на экран с помощью Сишной функции printf
        printf("a + b = %x + %x = %x\n", a, b, c);
}
Или, что то же самое, так:
Код:
// ассемблерная вставка
__asm mov eax, a
__asm add eax, b
__asm mov c, eax
А можно вообще в одну строчку:
Код:
// ассемблерная вставка
__asm mov eax, a __asm add eax, b __asm mov c, eax
Во встроенном ассемблере Visual C++ можно использовать все инструкции вплоть до Pentium 4 и AMD Athlon. Поддерживается также MMX. Поддержки Itanium и x64 пока нет
Mikl___ вне форума Ответить с цитированием
Старый 03.12.2014, 14:03   #38
Mikl___
Участник клуба
 
Регистрация: 11.01.2010
Сообщений: 1,162
По умолчанию

По синтаксису встроенный ассемблер Visual C++ частично совпадает с MASM. Например, как и в MASM можно строить выражения с операндами и использовать offset с глобальными переменными. Как и в MASM, можно использовать глобальные метки и принудительно определять короткие переходы. Однако определить локальную метку с помощью @@ во встроенном ассемблере Visual C++ не получится – замена lab: на @@lab: в предыдущем примере вызовет ошибку компиляции. Есть и другие отличия от MASM. Например, во встроенном ассемблере Visual C++ нет никаких средств для объявления переменных, поэтому о привычных DB, DW, DD, DQ, DT, DF, DUP и THIS можно забыть. Зато можно использовать переменные, объявленные в программе на C++.

Во встроенном ассемблере также нельзя объявлять структуры — директивы STRUC, RECORD, WIDTH и MASK недопустимы. Вместо этого можно использовать структуры, объявленные в программе на C++. Кроме того, в ассемблере можно использовать комментарии и HEX-числа в стиле C++.

В принципе, все отличия подробно описаны в MSDN (идет в комплекте с Visual Studio).

Обычно в Visual C++ ассемблер используется для написания функций. Как правило, функции, написанные на ассемблере, объявляют с использованием директивы _stdcall. В этом случае параметры передаются в функцию на стеке в обратном порядке, а результат работы возвращается в eax.

Для примера рассмотрим функцию, которая находит длину ASCIIZ-строки:
Код:
int _stdcall get_str_length(char *inputstr)
{// чаще всего функция целиком состоит из одной большой ассемблерной вставки
__asm{
; можно свободно обращаться к переменным, переданным в функцию
    mov edi, inputstr
    mov esi, edi
    or ecx, -1
    xor al, al
    cld
    repne scasb
    sub edi, esi
; результат работы функции следует возвращать в eax
    mov eax, edi
    dec eax
   }
}
Использовать эту функцию можно так:
Код:
int main()
{
   char str_1[]="Hello, world!";
   int i;
   i = get_str_length(str_1);
   printf("String: %s\nLength: %d", str_1, i); 
}
При написании кода на ассемблере Visual C++ следует помнить некоторые моменты. Во-первых, значения регистров не передаются между ассемблерными вставками. Например, если в ассемблерной вставке ты установил eax в 1, то в следующей ассемблерной вставке eax не обязательно будет равно 1.
Во-вторых, нужно быть осторожным с регистрами и стеком. В середине ассемблерных вставок нельзя менять регистры ds, ss, sp, bp и флаги. Если эти регистры все-таки меняются, перед выходом из ассемблерной вставки их нужно обязательно восстановить. Что касается стека, то тут нужно соблюдать правило, которое гласит: если в ассемблерной вставке нечто ложится на стек, то в той же ассемблерной вставке это «нечто» должно со стека сниматься.
Рассмотрим, например, такой код:
Код:
#include <stdio.h>
// наша функция на ассемблере
void _stdcall Test()
{
__asm{
; кладем на стек eax
push eax

; перед тем как ассемблерная вставка кончится, нужно
; снять eax со стека (например, с помощью pop), но мы забыли это сделать ;)
         }
}
int main()
{ 

// вызываем функцию... и любуемся сообщением об ошибке 
Test();
}
И, наконец, если пишется не драйвер, а обычное Win32-приложение, в ассемблерной вставке не должно быть привилегированных инструкций. Тем, кто хочет узнать больше — почитайте MSDN — там есть вся необходимая информация.
Mikl___ вне форума Ответить с цитированием
Старый 05.12.2014, 09:46   #39
Mikl___
Участник клуба
 
Регистрация: 11.01.2010
Сообщений: 1,162
По умолчанию

Написать программу, выводящую строку "Hello, World!" на экран
  • masm exe вывод строки на экран 9 функцией DOS
    Код:
    .MODEL SMALL
    .STACK 100h
    .DATA
        HelloMessage DB 'Hello World',13,10,'$'
    .CODE
    START:
        mov ax,@data
        mov ds,ax
        mov ah,9
        mov dx,OFFSET HelloMessage
        int 21h
        mov ah,4ch
        int 21h
    END START
  • вывод через int 29h
    Код:
    .286
    .MODEL SMALL
    .STACK 100h
    .DATA
            HelloMessage DB 'Hello World',10,13
        num = $ - HelloMessage
    .CODE
    START:  push @data
            pop ds
        mov cx,num
    a1: lodsb
            int 29h
        loop a1
            mov ah,4ch
            int 21h
    end start
  • прямой вывод строки в видео память, выводит красные символы на черном фоне
    Код:
    .286
    .MODEL SMALL
    .STACK 100h
    .DATA
            HelloMessage DB 'Hello World'
        num = $ - HelloMessage
    .CODE
    START:  push @data
            pop ds
        push 0B800h
        pop es
        mov ax,3
        int 10h
        mov di,0
        mov si,offset HelloMessage
        mov cx,num
        mov ah,0Ch
    a1: lodsb
            stosw
        loop a1
            mov ah,4ch
            int 21h
    end start
  • masm com с директивами стандартной сегментации
    Код:
    CSEG SEGMENT
    assume cs:cseg,ds:cseg
    org 100h
    start:  jmp begin
    msg db 'Hello World!'
    begin:  mov ah,40h
        mov bx,1
        mov cx,12
        mov dx,offset msg
        int 21h
        ret
    CSEG ENDS
    end start
  • Код:
    CSEG SEGMENT
    assume cs:cseg,ds:cseg
    org 100h
    start:  mov ah,40h
        mov bx,1
        mov cx,12
        mov dx,offset msg
        int 21h
        ret
    msg db 'Hello World!'
    CSEG ENDS
    end start
  • вывод функцией BIOS
    Код:
    .model tiny
    .286
    .code
    org 100h
    start:
    mov ah,13h
    xor dx,dx
    mov cx,sizeof string
    mov bp,offset string
    mov bx,0ch
    int 10h
    ret
    string db 'Hello World!'
    end start
  • Код:
    .data
    HELLO DB 'Здравствуй мир!',0 ;строка для вывода 
    .code
    MOV AH,0Eh ; на экран номер подфункции BIOS
    mov si,offset HELLO ;SI указывает на строку
    next: lodsb ;помещаем символ в AL и переходим к следующему символу,
     INT 10h ;выводим символ на экран
    test al,al ;проверяем на конец строки
    jnz next ;если нет — повторяем все сначала
  • fasm консольная программа для Windows
    Код:
    ; hello - example of tiny (one section) Win32 program
     
    format PE console 4.0
     
    include 'include\win32ax.inc'
    ENABLE_PROCESSED_OUTPUT  = 00000001
     
        invoke  AllocConsole
        invoke  SetConsoleCP, 1251
        invoke  SetConsoleOutputCP, 1251
        invoke  GetStdHandle, STD_INPUT_HANDLE
            push    eax
            push    eax
        invoke  GetStdHandle, STD_OUTPUT_HANDLE
            push    eax
        invoke  SetConsoleMode, eax, ENABLE_PROCESSED_OUTPUT
        pop eax
        invoke  WriteConsole, eax, hello, msgsz, NULL, NULL
    ;   pop eax
        invoke  FlushConsoleInputBuffer
        pop eax
        invoke  ReadConsole, eax, buf, bufsz, cnt, NULL
    ;exit:
        invoke  FreeConsole
        invoke  ExitProcess,0
     
    cnt dd  ?
    buf db  10 dup(?)
    bufsz   =   $ - buf
    hello   db  'cp1251 Привет из консоли!',0
    msgsz   =   $ - hello
    ; import data in the same section
     
    data import
     
     library kernel32,'KERNEL32.DLL'
     
     import kernel32,\
        ExitProcess,'ExitProcess',\
        SetConsoleCP,'SetConsoleCP',\
        SetConsoleOutputCP,'SetConsoleOutputCP',\
        GetStdHandle,'GetStdHandle',\
        SetConsoleMode,'SetConsoleMode',\
        ReadConsole,'ReadFile',\
        WriteConsole,'WriteConsoleA',\
        FreeConsole,'FreeConsole',\
        FlushConsoleInputBuffer,'FlushConsoleInputBuffer',\
        AllocConsole,'AllocConsole'
    end data
  • разноцветная надпись в DOS
    Код:
    .286 
    .model tiny
    .code
    org 100h 
    start: mov bp,offset ABC
        mov ax,1303h
        mov bx,7
        mov cx,16
        xor DX,DX
        int 10h 
        retn
    ABC db 'H',0Ah,'e',0Bh,'l',0Dh,'l',0Ch
        db 'o',0Bh,',',0Ah,' ',0Ah,'W',09h
        db 'o',08h,'r',07h,'l',06h,'d',05h
        db '!',02h,'!',02h,'!',02h
    end start
  • Код:
    .286
    .model tiny
    .code
    org 100h
    start:  mov si,offset string
        mov cx,N
        mov ah,2
    @@: lodsb
        mov dl,al
        int 21h
        loop @b
        retn
    string db 'Hello, world!'
    N = $ - string
    end start
  • Код:
    .286
    .model tiny
    .code
    org 100h
    start:  mov si,offset string
        mov cx,N
        mov ah,6
    @@: lodsb
        mov dl,al
        int 21h
        loop @b
        retn
    string db 'Hello, world!'
    N = $ - string
    end start
Mikl___ вне форума Ответить с цитированием
Старый 05.12.2014, 09:49   #40
Mikl___
Участник клуба
 
Регистрация: 11.01.2010
Сообщений: 1,162
По умолчанию

  • Пример вызова функции DOS через альтернативный обработчик прерывания 21h.
    Код:
    .286
    .model tiny
    .code
    org 100h
    start: push offset RETURN     ;Занести в стек флаги, сегмент
            push cs                    ;и смещение адреса возврата
            pushf                      ;в обратном порядке.
            mov cl,9                 ;Функция: показать строку.
            mov dx,offset MESSAGE    ;Загрузить адрес сообщения.
            db 0EAh,0C0h,0,0,0     ;jmp far ptr 0:0C0h
    RETURN: mov ah,4Ch               ;Завершить процесс через DOS.
            int  21h                   
    MESSAGE db 'Hello, world!$'
    end start
  • имитируем вызов int 21h
    Код:
    .286
    .model tiny
    .code
    org 100h
    start:
    ;получаем дальний адрес (cs:ip) обработчика 
    ;прерывания 21h из таблицы векторов прерывания
        push 0;сегментный адрес таблицы векторов прерывания в es    
        pop es      
        mov di,es:[21h*4];смещение обработчика прерывания 21h в di
        mov si,es:[21h*4+2];сегмент обработчика прерывания 21h в si
        mov dx,offset string
        mov ah,9      ;номер функции
    ;три параметра в стек для возврата из прерывания
        pushf           
            push cs
            push offset @f  ;адрес возврата
        push si     ;cs для int 21h
        push di     ;ip для int 21h
        retf        ;подменяем cs и ip
    @@: mov ah,4Ch      ;выход из программы
        int 21h
    string db 'Hello, world!$'
    end start
  • DPMI client FASM
    Код:
    format  MZ
     
        LF  equ 10
        CR  equ 13
     
        entry   _TEXT:start
     
        segment _TEXT use16
     
    start:
        push    cs
        pop ds
        mov ax, ss
        mov cx, es
        sub ax, cx
        mov bx, sp
        shr bx, 4
        inc bx
        add bx, ax          ;release unused DOS memory
        mov ah, 4Ah
        int 21h
        mov ax, 1687h       ;DPMI host installed?
        int 2Fh
        and ax, ax
        jnz nohost
     
        push    es          ;save DPMI entry address
        push    di
        and si, si          ;requires host client-specific DOS memory?
        jz  nomemneeded
        mov bx, si
        mov ah, 48h         ;alloc DOS memory
        int 21h
        jc  nomem
        mov es, ax
    nomemneeded:
        mov bp, sp
        mov ax, 0001        ;start a 32-bit client
        call    far [bp]        ;initial switch to protected-mode
        jc  initfailed
    ;now in protected-mode
    ; CS = 16-bit selector corresponding to real-mode CS
    ; SS = selector corresponding to real-mode SS (64K limit)
    ; DS = selector corresponding to real-mode DS (64K limit)
    ; ES = selector to program's PSP (100h byte limit)
    ; FS = GS = 0
        mov cx,1            ;get a descriptor for the 32-bit code segment
        mov ax,0
        int 31h
        jc  dpmierr
        mov bx,ax                   ; base selector
        mov dx,_TEXT32
        mov cx,dx
        shl dx,4
        shr cx,12                   ; now CX:DX = linear(flat) _TEXT32 addr
        mov ax,7            ;set base
        int 31h
        or  dx,-1
        xor cx,cx
        mov ax,8            ;set limit
        int 31h
        mov cx,cs
        lar cx,cx
        shr cx,8
        or  ch,40h          ;make a 32bit CS
        mov ax,9
        int 31h
        push    bx          ; _TEXT32 selector
        push    start32
        retf                ;jump to 32bit CS
    nohost:
        mov dx, dErr1
    error:
        mov ah, 9
        int 21h
        mov ax, 4C00h
        int 21h
    nomem:
        mov dx, dErr2
        jmp error
    initfailed:
        mov dx, dErr3
        jmp error
    dpmierr:
        mov dx, dErr4
        jmp error
     
    ;   segment _DATA use16
     
    szWelcome   db      "Good bye, ugly 16 bit!",CR,LF
            db  "Hello, world from DOS protected mode 32-bit client!",CR,LF,'$'
    dErr1       db  "no DPMI host installed",CR,LF,'$'
    dErr2       db  "not enough DOS memory for client initialisation",CR,LF,'$'
    dErr3       db  "DPMI initialisation failed",CR,LF,'$'
    dErr4       db  "no LDT descriptors available",CR,LF,'$'
     
    ;--- the 32-bit code segment
     
        segment _TEXT32 use32
     
    start32:
        mov edx, szWelcome      ;print welcome message
        mov ah,9
        int 21h
        mov ah,0
        int 16h
        mov ax, 4C00h       ;return to DOS
        int 21h
  • Код:
    .386
        .model flat, stdcall
        option casemap :none
        include \masm32\include\windows.inc
        include \masm32\include\kernel32.inc
        includelib \masm32\lib\kernel32.lib
    .data
    msg db "Hello World"
    stdout dd ?
    cWritten dd ?
    .code
    start:
    invoke GetStdHandle,STD_OUTPUT_HANDLE
    mov stdout,eax
    invoke WriteConsoleA,stdout,ADDR msg,SIZEOF msg,ADDR cWritten,NULL
    invoke ExitProcess,0
    end start
  • Код:
    .386
        .model flat, stdcall
        option casemap :none
        include \masm32\include\masm32.inc
        include \masm32\include\kernel32.inc
        include \masm32\macros\macros.asm
        includelib \masm32\lib\masm32.lib
        includelib \masm32\lib\kernel32.lib
        .code
        start:
          print "Hello world"
          exit
        end start
Mikl___ вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Программа для шифрования раздела с виндой Жека90 Софт 1 12.06.2012 18:17
макрос для решения квадратных уравнений (перемещено из раздела Excel) sashkkk Помощь студентам 3 22.09.2010 23:06
Собираем команду для FAQ Aexx Свободное общение 112 14.10.2009 09:20
Формат по образцу для раздела... Busine2009 Microsoft Office Word 0 28.07.2009 08:05
Программа для копирования заданного раздела. С++ x007 Общие вопросы C/C++ 5 23.04.2009 23:52