Форум программистов, компьютерный форум, киберфорум
FASM
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.86/65: Рейтинг темы: голосов - 65, средняя оценка - 4.86
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757

Программирование на языке Assembler в FASM

18.08.2015, 05:51. Показов 13656. Ответов 3
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Программирование на языке Assembler в FASM
автор pas
взято здесь

Введение
В начале было слово... Если точнее то было просто предложение от Kinder-а написать статью посвящённую макросам в FASM. Я согласился попробовать, но рассматривать макросы отдельно от синтаксиса как-то не очень правильно, а синтаксис без примеров разобрать сложно и в результате получилось сочинение о том как писать программы в FASM.

В этом сочинении будут рассмотрены основные моменты отличающие FASM от других компиляторов, правила по которым пишутся макросы и форматы выходных файлов создаваемые FASM-ом. Так же в рамках данного сочинения мы создадим несколько контролов в виде макросов.

Основы


Данный раздел является вольным пересказом соответствующего раздела руководства, поставляемого с компилятором.

Данная статья посвящена использованию макросов в компиляторе FASM. Для начала вопрос: "Что такое макрос?" (поскольку я не могу услышать Ваш ответ прямо сейчас, когда пишу эту статью, отправляйте свои ответы мне по электронной почте, если Ваше мнение отличается от моего мнения).

Макрос - это инструкция препроцессору компилятора развернуть, встретившееся в коде программы, имя макроса (возможно с параметрами) в последовательность строк кода ассемблера с использованием переданных параметров (если таковые заданы).

Далее предлагаю Вам ознакомиться с основными директивами, используемыми внутри определений макросов.

Внутри определений макросов можно использовать инструкции if и else при этом каждая инструкция if должна закрываться инструкцией end if. Пример из руководства, поставляемого вместе с компилятором:
Assembler
1
2
3
4
5
6
7
8
9
macro mov op1,op2
{
if op1 in  & op2 in 
   push op2
   pop op1
else
   mov op1,op2
end if
}
Этот макрос является расширением инструкции процессора mov. В данном случае если оба операнда макроса являются сегментными регистрами используется связка "push - pop", в любом другом случае используется стандартная инструкция "mov op1,op2". Оператор in позволяет проверить соответствие операнда нескольким значениям в угловых скобках.

Можно ещё более расширить эту инструкцию для пересылки трёх значений второго в первый, а третьего во второй:
Assembler
1
2
3
4
5
6
7
8
9
macro mov op1,op2,op3
{
   if arg3 eq
      mov op1,op2
   else
      mov op1,op2
      mov op2,op3
   end if
}
В данном случае нас интересует инструкция eq, которая позволяет проверить эквивалентен ли операнд какому либо значению, в нашем случае мы проверяем его отсутствие, и если он отсутствует, то подставляется ветвь if иначе подставляется ветвь else.

Директива purge позволяет отменить последнее определение макроса.

Кроме уже изложенного в определение макроса можно передавать заранее неизвестное количество операндов (пар операндов и т.д.), для этого необходимо заключить операнды, количество которых заранее неизвестно, в квадратные скобки. Например для вызова процедур написан макрос STDCALL:
Assembler
1
2
3
4
5
macro stdcall proc,[arg]; 
 { reverse
    push arg
   common
    call proc }
Операндами этого макроса служат один обязательный (proc) и несколько необязательных параметров (arg). Директива reverse сообщает препроцессору, что следующие строки необходимо повторить столько раз, сколько параметров arg передано макросу начиная с последнего параметра. Директива common сообщает препроцессору, что следующие строки необходимо повторить только один раз. Директива forward сообщает препроцессору, что следующие строки необходимо повторить столько раз, сколько параметров arg передано макросу начиная с первого параметра. Действие директив common, forward и reverse заканчивается другой директивой common, forward или reverse соответственно или закрывающейся фигурной скобкой. Если ни одна из этих директив не встречается в определении макроса, то макрос развернётся для всех параметров начиная с первого. Неизвестное количество параметров можно передать и другому макросу:
Assembler
1
2
3
4
5
6
7
macro invoke proc,[arg]         
 { common
    if ~ arg eq
     stdcall [proc],arg
    else
     call [proc]
    end if }
В этом примере при выполнении ветви if в макрос stdcall кроме параметра [proc] передаются все полученные параметры arg.

Внутри макроопределения может использоваться оператор #, который может использоваться для составления операторов ассемблера, например условного перехода:
Assembler
1
2
3
4
5
macro jif op1,cond,op2,label
{
  cmp op1,op2
  j#cond label
}
При выполнении макроса jif ax,ae,10h,exit макрос будет развёрнут в следующую конструкцию:
Assembler
1
2
cmp ax,10h
jae exit
Так же этот оператор можно использовать и для составления имён переменных или макросов внутри макроопределений:
Assembler
1
2
3
4
macro ver [name]
{
 name#.mas: times 10 db 0
}
Теперь при вызове данного макроса в секции данных
Assembler
1
ver Chif,Rab
Мы будем иметь два массива из 10 байт каждый, с именами Chif.mas и Rab.mas.

Аналогично можно создать и переменные и макросы внутри определения макроса. Однако определить макрос внутри макроса обычным путём:
Assembler
1
2
3
4
5
6
7
Macro mac1
{
 macro mac2
  {
  ... ... ...
  }
}
препроцессор не позволит использовать данную конструкцию, выход в использовании директивы fix она эквивалентна директиве equ, но препроцессор обрабатывает fix позже чем equ, что и позволяет использовать подобную конструкцию:
Assembler
1
2
3
4
5
6
7
8
9
 Macro mac1
{
 macro mac2
m_
  ... ... ...
_m
}
m_ fix {
_m fix }
Синтаксис компилятора FASM несколько отличается от синтаксиса других компиляторов, но отличия незначительны и в основном касаются макроязыка.

Средства макроязыка мы рассмотрим ниже, здесь же я хотел бы отметить только то, что передача адреса переменной производится указанием имени переменной без каких либо скобок или префиксов, а её содержимого написанием имени переменной в квадратных скобках.
Assembler
1
2
mov eax,var; записываем в регистр еах адрес переменной 
var mov eax,[var]; записываем в регистр еах значение переменной var

Структура каркасного приложения на FASM'е

В этой части мы рассмотрим, основы синтаксиса данного компилятора и рассмотрим каркасные приложения для основных форматов исполняемых файлов.

Итак, что необходимо для работающего приложения?

Во-первых мы должны указать компилятору какого формата должен быть создаваемый файл. Это делается директивой format после, которой следует указание форматов:
  • PE формат исполняемых файлов Windows. Далее должно следовать уточнение GUI (графический интерфейс), console (консольное приложение) и native (если Вы знаете, что это такое сообщите мне). Если выбран графический интерфейс, то следует также указать версию графического интерфейса 4.0, а так же зарезервированное слово DLL, если собирается динамическая библиотека.
  • MZ формат исполняемых файлов MS DOS.
  • COFF или MS COFF (для линковки продуктами Microsoft) для создания объектного файла, который в дальнейшем будет линковаться к другому проекту или к ресурсам.
  • ELF формат для создания исполняемых файлов UNIX подобных систем (Linux).
Как Вы наверно поняли, я не знаком с файлами ELF типа и в данной статье мы не будем его рассматривать. Если директива format не указана, компилятор соберёт файл в формате com.

После указания формата выходного файла мы должны указать компилятору точку начала программы. Это делается с помощью директивы entry после которой указывается метка, с которой начинается код программы. Например entry start.

Так же, в случае необходимости, мы должны указать на начало секций данных, кода, импорта, экспорта или ресурсов. Для этого существует директива section, после которой в кавычках следует название секции, далее без скобок тип секции (данные или код) и атрибуты (запись, чтение исполняемый код). Более подробно когда какие атрибуты указывать Вы прочитаете ниже т.к. для разных форматов выходных файлов эти атрибуты отличаются.


Форматы PE исполняемых файлов Windows.
Консольные приложения
Консольное приложение может использовать все функции API предоставляемые Windows, но работать с такой программой пользователь может только через командную строку.

Вот простой пример консольного приложения:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
format PE console
entry start
include 'include\kernel.inc'
include 'include\macro\stdcall.inc'
include 'include\macro\import.inc'
section '.data' data readable writeable
  h4    db 'Консоль'
  w1 dd 0
  output dd 0
  input dd 0
  _class db 'FCAPTION',0
  podskazka db 'Консоль',0
section '.code' code readable executable
  start:
    invoke    GetStdHandle,STD_OUTPUT_HANDLE
    mov       [output],eax
    invoke    GetStdHandle,STD_INPUT_HANDLE
    mov       [input],eax
    invoke    WriteConsole,[output],h4,7,w1,0
    invoke    ReadConsole,[input],_class,2,w1,0
    invoke  ExitProcess,0
section '.idata' import data readable
 
  _library kernel32,'KERNEL32.DLL',\
         user32,'USER32.DLL',\
         gdi32,'GDI32.DLL',\
         advapi32,'ADVAPI32.DLL',\
         comctl32,'COMCTL32.DLL',\
         comdlg32,'COMDLG32.DLL',\
         shell32,'SHELL32.DLL',\
         wsock32,'WSOCK32.DLL';,\
     include 'C:\FASM135\147\INCLUDE\apia\kernel32.inc'
     include 'C:\FASM135\147\INCLUDE\apia\user32.inc'
     include 'C:\FASM135\147\INCLUDE\apia\gdi32.inc'
     include 'C:\FASM135\147\INCLUDE\apia\advapi32.inc'
     include 'C:\FASM135\147\INCLUDE\apia\comctl32.inc'
     include 'C:\FASM135\147\INCLUDE\apia\comdlg32.inc'
     include 'C:\FASM135\147\INCLUDE\apia\shell32.inc'
     include 'C:\FASM135\147\INCLUDE\apia\wsock32.inc'
В данном примере создаётся три секции: данных, кода и секция импортируемых функций. Секцию импорта я создаю при помощи макроса _library, в стандартной поставке такого макроса нет, но если написать без подчёркивания перед library то всё скомпилируется правильно. Причина проста, я пользуюсь FASM-ом с версии 1.35, а тогда такого макроса еще не было (а сам я ещё не смог бы его написать) и для совместимости с ранее написанными программами я включил два варианта этого макроса. В ранней версии, необходимо было записывать все используемые в приложении функции вручную, теперь же работу по подключению только используемых функций берёт на себя компилятор, хотя за такое удобство приходится платить некоторым увеличением времени компиляции. Более подробно о принципе работы этого и других макросов будет написано в соответствующем разделе ниже.

Название секции импорта ".idata", хотя название можно изменить и это не повлияет на работоспособность программы, т.к. мы указали назначение этой секции import data, далее следует указание атрибутов, в данном случае readable, что означает для чтения и этого достаточно, если Вы не собираетесь менять содержимое этой секции.

Следующей рассмотрим секцию данных. У нас секция данных имеет название ".data", далее следует указание назначения секции data, что означает секцию данных, атрибуты указаны readable writeable, т.е. эта область памяти будет доступна для чтения и записи. Если указать только первый атрибут (readable) при выполнении программы произойдёт ошибка "Программа выполнила недопустимую операцию".

Последней рассмотрим секцию кода, она имеет название ".code" назначение секции code, т.е. исполняемая часть программы, атрибуты readable executable, что означает, что из секции кода мы можем читать данные (под данными может подразумеваться и коды инструкций и определённые в этой секции данные) и можем выполнять содержимое этой секции.

Кроме рассмотренных секций, консольное приложение может иметь секции экспортируемых функций и секцию ресурсов.

Это пожалуй всё, что необходимо знать для начала о консольных приложениях. Да ещё я считаю лишний раз напомнить, что хоть консольное приложение и напоминает внешне программы DOS-а, использование прерываний в нём не допускается так как это всё же приложение Windows, естественно выполняться такое приложение может только в Windows.
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
18.08.2015, 05:51
Ответы с готовыми решениями:

Конвертер для перевода программ на языке PHP в текст программы на языке Assembler
Всем привет. Как можно реализовать такую задачу? Разработать конвертор в языке Assembler для перевода программ на языке PHP в текст...

С помощью цикла определить значение функции F=a*(2^n)
добрый день всем участкам данного форума! помогите, пожалуйста, написать программу на Assembler Fasm. ЗАДАНИЕ: Создать программу на...

Обработка клавиш Fasm Assembler 16bit
Здравствуйте.Делаю свою ос на чистом ассемблере.Дошло дело до обработки клавиши Enter,я новичок если что.Как такое реализовать? Код для...

3
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
18.08.2015, 06:45  [ТС]
Графическое приложение
Графические приложения Windows это оконные приложения, такие как Word, Excel, Notepad и т.д.

В отличии от консольных приложений, GUI приложения должны содержать как минимум одну процедуру обработки сообщений, приходящих к окну. Вот простой пример оконного приложения:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
format PE GUI 4.0
entry start
include 'include\kernel.inc'
include 'include\user.inc'
include 'include\macro\stdcall.inc'
include 'include\macro\import.inc'
include 'include\macro\resource.inc'
section '.data' data readable writeable
  Avtor db 'Автор:Пороткин Алексей Сергеевич',0
  Progr db 'Программа построения графиков',0
  MyMessage db 'Отклик',0
  hinstance dd 0
  h4    db 'Сообщение',0
  w1 dd 0
  msg MSG
  wc WNDCLASS
  pt dd 0
  hDC dd 0
  ws equ  WS_VISIBLE+WS_CAPTION+WS_MINIMIZEBOX+WS_SYSMENU
  _class db 'FCAPTION',0
  podskazka db 'Графическое приложение',0
section '.code' code readable executable
  start:
    invoke  GetModuleHandle,0
    mov [hinstance],eax
    invoke  LoadIcon,eax,17
    mov [wc.hIcon],eax
    invoke  LoadCursor,0,IDC_ARROW
    mov [wc.hCursor],eax
    mov [wc.style],0
    mov [wc.lpfnWndProc],WindowProc
    mov [wc.cbClsExtra],0
    mov [wc.cbWndExtra],0
    mov eax,[hinstance]
    mov [wc.hInstance],eax
    mov [wc.hbrBackground],6;COLOR_BTNFACE+1
    mov [wc.lpszMenuName],0
    mov [wc.lpszClassName],_class
    invoke  RegisterClass,wc
    invoke  CreateWindowEx,0,_class,podskazka,ws,10,10,680,450,NULL,NULL,[hinstance],NULL
 
  msg_loop:
    invoke  GetMessage,msg,NULL,0,0
    or  eax,eax
    jz  end_loop
    invoke  TranslateMessage,msg
    invoke  DispatchMessage,msg
 
    jmp msg_loop
 
  end_loop:
    invoke  ExitProcess,[msg.wParam]
;include 'include\macro\lib.asm'
proc WindowProc, hwnd,wmsg,wparam,lparam
    enter
    push    ebx esi edi
          cmp   [wmsg],WM_DESTROY
    je  wmdestroy
    cmp [wmsg],WM_CREATE
    je  wmcreate
  defwndproc:
    invoke  DefWindowProc,[hwnd],[wmsg],[wparam],[lparam]
    jmp finish
wmcreate:
    
    jmp ext
 
  wmdestroy:
    invoke  PostQuitMessage,0
ext:    xor eax,eax
  finish:
    pop edi esi ebx
    return
section '.idata' import data readable writeable
  library kernel,'KERNEL32.DLL',\
      user,'USER32.DLL'
      ;shell,'SHELL32.DLL',\
      ;gdi,'gdi32.dll'
  kernel:
  import GetModuleHandle,'GetModuleHandleA',\
     ExitProcess,'ExitProcess',lstrcpy,'lstrcpyA',\
     FindFirstChangeNotification,'FindFirstChangeNotificationA',\
     FindNextChangeNotification,'FindNextChangeNotification',\
     FindCloseChangeNotification,'FindCloseChangeNotification'
  user:
  import RegisterClass,'RegisterClassA',\
     CreateWindowEx,'CreateWindowExA',\
     DefWindowProc,'DefWindowProcA',\
     SetWindowLong,'SetWindowLongA',\
     RedrawWindow,'RedrawWindow',\
     GetMessage,'GetMessageA',\
     MessageBox,'MessageBoxA',\
     TranslateMessage,'TranslateMessage',\
     DispatchMessage,'DispatchMessageA',\    
     SendMessage,'SendMessageA',\
     LoadCursor,'LoadCursorA',\
     LoadIcon,'LoadIconA',\
     LoadMenu,'LoadMenuA',\
     SetMenu,'SetMenu',\
     InsertMenu,'InsertMenuA',\
     CreateMenu,'CreateMenu',\
     DrawMenuBar,'DrawMenuBar',\     
     PostQuitMessage,'PostQuitMessage',\
     FindWindow,'FindWindowA',\
     FindWindowEx,'FindWindowExA',\
     LoadImageA,'LoadImageA',\
     CreatePopupMenu,'CreatePopupMenu',\
     AppendMenu,'AppendMenuA',\
     DestroyMenu,'DestroyMenu',\
     GetCursorPos,'GetCursorPos',\
     SetForegroundWindow,'SetForegroundWindow',\
     TrackPopupMenu,'TrackPopupMenu',\
     PostMessage,'PostMessageA',\
     DestroyWindow,'DestroyWindow',\
     ShowWindow,'ShowWindow',GetDC,'GetDC'
section '.rsrc' resource data readable
  directory   RT_ICON,icons,\
        RT_GROUP_ICON,group_icons
  icons:
  resource 1,LANG_NEUTRAL,icon_data
  group_icons:
  resource 17,LANG_NEUTRAL,main_icon
  icon main_icon,icon_data,'mp\lv\lv.ico'
Так же, как и в примере с консольным приложением, в первой строке указана директива format PE, но вместо console, как в предыдущем примере, дано указание GUI 4.0 компилятору собирать приложение с графическим интерфейсом версии 4.0.

Приложение из нашего примера ничего полезного не делает, оно просто создаёт окно, которое реагирует только на нажатие двух системных кнопок в верхнем правом углу окна "Свернуть" и "Закрыть". Приложение содержит секции данных, кода, импорта и ресурсов. После указания точки входа в программу entry start подключаются файлы содержащие макросы и определения констант и структур Windows. Затем следует секция данных, определение которой дано в предыдущем разделе, и секция кода. В секции кода мы получаем хендл нашего модуля (GetModuleHandle), загружаем иконку (LoadIcon), определённую в секции ресурсов, загружаем стандартный курсор Windows (LoadCursor) и заполняем структуру wc для дальнейшей регистрации класса нашего окна (RegisterClass). После регистрации класса окна мы создаём окно зарегистрированного класса (CreateWindowEx). В структуре wc имеется член .lpfnWndProc, в который мы записываем адрес начала процедуры обработки событий окна WindowProc. Далее следует цикл обработки сообщений, посылаемых окну системой Windows.

Процедура обработки сообщений получает от Windows четыре параметра:
Assembler
1
proc WindowProc, hwnd, wmsg, wparam, lparam
где:
  • hwnd - хендл нашего окна
  • wmsg - код сообщения, которое Windows послало нашему окну
  • wparam и lparam - уточняющие параметры посланного сообщения.
proc это не директива компилятора, а макрос содержащий, часть пролога процедуры. Он определён следующим образом:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
macro proc name,[arg]
 { common
    name:
    virtual at ebp+8
    if ~ arg eq
   forward
     local ..arg
     ..arg dd ?
     arg equ ..arg
   common
     end if
     ..ret = $ - (ebp+8)
    end virtual
    local ..dynamic_data,..dynamic_size
    dynamic_data equ ..dynamic_data
    dynamic_size equ ..dynamic_size
    virtual at ebp - dynamic_size
     dynamic_data: }
Таким образом следующая строка proc Win,param будет развёрнута в следующую конструкцию:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
    Win:
    virtual at ebp+8
        local ..param
        ..param dd ?
        param equ ..param
        ..ret = $ - (ebp+8)
    end virtual
    local ..dynamic_data,..dynamic_size
    dynamic_data equ ..dynamic_data
    dynamic_size equ ..dynamic_size
    virtual at ebp - dynamic_size
     dynamic_data: }
Более подробно это означает, что мы определяем метку Win, которая означает начало процедуры. В следующей строке записана директива virtual at ebp+8, которая должна заканчиваться директивой end virtual. Внутри этого определения помещаются переменные, которые будут размещаться с адреса указанного после at, в нашем примере это ebp+8. Это означает, что в нашем примере запись "mov param,eax" будет заменена на "mov [ebp+8],eax". Если бы в определении процедуры было две или более переменных, то адрес каждой последующей переменной увеличивался бы на размер предыдущей переменной. Таким образом:
Assembler
1
2
3
4
5
virtual at ebp+8
   param1 dd ?
   param2 db ?
   param3 dd ?
end virtual
определит три переменные с адресами
Assembler
1
2
3
param1 = ebp+8+0,
param2 = ebp+8+4,
param2 = ebp+8+5.
У некоторых читателей может возникнуть вопрос: "А зачем все эти пляски?". Ответ прост: регистр ebp указывает на вершину стека и все переменные, передаваемые в процедуру, находятся выше (в смысле в памяти с адресами больше чем вершина стека так как вершина стека сдвигается вниз когда в стек помещаются данные и вверх, когда данные из стека изымаются) текущей вершины стека.

Первой, в определении переменных внутри virtual - end virtual записана директива local, которая указывает компилятору, что следующие за ней через запятую метки, локальны в данном макросе.

Последней, внутри описания виртуальных переменных, следует определение макропеременной ..ret = $ - (ebp+8), которая фактически содержит количество переданных процедуре байт параметров. Она используется при написании эпилога процедуры для очистки стека при возврате управления инструкции, следующей за вызовом данной процедуры.

После определения виртуальных переменных, передаваемых процедуре в качестве параметров, определяются локальные макропеременные, которые используются во второй части пролога процедуры. Я уже несколько раз использовал термин - макропеременная. Макропеременная отличается от переменных, используемых в программе, тем, что она существует только во время компиляции и используется как константа, которая может иметь разные значения в разных частях программы.

После определения макропеременных начинается блок определения виртуальных переменных, однако переменных в этом макросе нет. После макроса proc необходимо ставить макрос enter, который содержит заключительную часть пролога, а между ними могут располагаться локальные переменные. Для того, что бы имена переменных были локальными перед ними ставится точка. Например .param. Макрос enter определён следующим образом:
Assembler
1
2
3
4
5
macro enter             
{  dynamic_size = $ - dynamic_data
   end virtual
   enter dynamic_size,0 
}
Таким образом dynamic_size - это размер памяти отведённой под локальные переменные. Последняя инструкция, в прологе процедуры, enter dynamic_size,0. Это единственная строка, которая попадёт в исполняемый код программы. Завершаться процедура должна макросом return, который определён следующим образом:
Assembler
1
2
3
macro return                
{ leave
   ret ..ret }
Полагаю здесь не должно быть сложностей с пониманием макроса.

Процедура должна правильно обрабатывать поступившие сообщения. Те сообщения, которые мы хотим обработать сами, выделяются в первых строках процедуры и управление передаётся на те участки кода, которые должны производить необходимые действия, а те сообщения, которые нам нет необходимости обрабатывать, мы доверяем обработать системе Windows (DefWindowProc).

Хочу обратить Ваше внимание на то, что в данной программе используются глобальные метки (wmdestroy, wmcreate, defwndproc), что не очень удобно в программах содержащих несколько процедур (большинство оконных процедур обрабатывают одни и те же сообщения), поэтому рекомендуется внутри процедур использовать локальные метки (.wmdestroy, .wmcreate, .defwndproc).

Теперь пришло время посмотреть на секцию импорта. Она отличается от рассмотренной в предыдущем разделе здесь мы используем макрос library, который определён следующим образом:


Assembler
1
2
3
4
5
6
7
8
macro library [label,string]
 { forward
    local _label
    dd 0,0,0,rva _label,rva label
   common
    dd 0,0,0,0,0
   forward
    _label db string,0 }
Для того, чтобы было более понятно вспомним, как в нашем примере используется этот макрос:


Assembler
1
2
library kernel,'KERNEL32.DLL',\
      user,'USER32.DLL'
Таким образом, макрос library создаёт заголовок таблицы импорта, вначале записывая смещение названия библиотеки DLL, а затем смещение, с которого начинаются имена импортируемых функций.

Далее следуют макросы import, определённые следующим образом:
Assembler
1
2
3
4
5
6
7
8
9
macro import [label,string]
 { forward
    local _label
    label dd rva _label
   common
    dd 0
   forward
    _label dw 0
       db string,0 }
Он создаёт тело таблицы импорта для каждой из импортируемых библиотек.

Вначале, создавая таблицу смещений имён импортируемых функций, а затем записывая имена импортируемых функций. Более подробно о структуре PE-файла Вы должны прочитать в соответствующей литературе (например в туториалах, которые написал Iczelion).

После таблицы импорта создаётся таблица ресурсов:
Assembler
1
2
3
4
5
6
7
8
section '.rsrc' resource data readable
  directory   RT_ICON,icons,\
        RT_GROUP_ICON,group_icons
  icons:
  resource 1,LANG_NEUTRAL,icon_data
  group_icons:
  resource 17,LANG_NEUTRAL,main_icon
  icon main_icon,icon_data,'mp\lv\lv.ico'
В нашем примере мы используем только одну иконку.
0
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
18.08.2015, 08:31  [ТС]
Динамические библиотеки (DLL)

Последним в данном разделе рассмотрим простую динамическую библиотеку.

Динамическая библиотека - это приложение GUI, который может содержать как исполняемый код, так и ресурсы. Это приложение не предназначено для самостоятельного запуска, оно используется другими приложениями для хранения часто используемых процедур, шрифтов и многого другого.

Мы рассмотрим создание динамической библиотеки на примере процедуры, возвращающей строку, содержащую текущую дату. Мы создадим два модуля один файл динамической библиотеки (dll2.dll) и файл использующий созданную нами библиотеку (usedll.exe).

Вот файл динамической библиотеки:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
format PE GUI 4.0 DLL
entry DllEntryPoint
include 'include\kernel.inc'
include 'include\user.inc'
include 'include\macro\stdcall.inc'
include 'include\macro\import.inc'
include 'c:\fasm135\include\lib\strings.mac'
include 'include\macro\export.inc'
section '.data' data readable writeable 
  _outs: times 60 db 0
  Date SYSTEMTIME
  day db 'День:',0
  mon db ' Месяц:',0
  year db ' Год:',0
  h4: times 20 db 0
  DllState db 'Состояние библиотеки DLL',0
  DllStart db 'Загружена',0
  DllStop  db 'Выгружена',0
  bf: times 20 db 0
section '.code' code readable executable
proc DllEntryPoint, hinstDLL,fdwReason,lpvReserved
    enter
    cmp       [fdwReason],DLL_PROCESS_ATTACH
    je    .start
    cmp       [fdwReason],DLL_PROCESS_DETACH
    je    .stop
    jmp   .stp
    .start:
    invoke    MessageBox,0,DllStart,DllState,MB_OK
    jmp   .stp
    .stop:
    invoke    MessageBox,0,DllStop,DllState,MB_OK
    jmp   .stp
   .stp:    
    mov eax,TRUE
    return
proc Wind
enter
    push    ebx esi edi
    invoke   GetLocalTime,Date
    mov  ebx,_outs
    StringCopyHTML ebx,day
    add  ebx,eax
    xor  eax,eax
    mov  ax,[Date.wDay]
    stdcall  IntToString,h4,eax
    StringCopyHTML ebx,h4
    add  ebx,eax
    StringCopyHTML ebx,mon
    add  ebx,eax
    xor  eax,eax
    mov  ax,[Date.wMonth]
    stdcall  IntToString,h4,eax
    StringCopyHTML ebx,h4
    add  ebx,eax
    StringCopyHTML ebx,year
    add  ebx,eax
    xor  eax,eax
    mov  ax,[Date.wYear]
    stdcall  IntToString,h4,eax
    StringCopyHTML ebx,h4
    mov  eax,_outs
    pop edi esi ebx
return
proc IntToString,lpOutBuf,var
   len dd 0 
       enter
       push ebx esi edx ecx esi
       ;jmp .ex
       cmp  [var],0
       jne  .norr
       mov   eax,[lpOutBuf]
       mov   dx,030h
       mov   [eax],dx
       jmp  .ex
       .norr:
       mov  eax,10
       mov  [len],eax  
       xor eax,eax
       xor edx,edx
       xor ecx,ecx
       mov  esi,bf
       mov  eax,[var]
       cmp eax,0
       jne .ll
       mov eax,48
       mov [esi],al
       inc esi
       jmp .ext
 .ll:  
       div  [len]
       add  dl,48
       mov  [esi],dl
       inc  esi
       inc ecx
       xor  edx,edx
       cmp  eax,0
       jne   .ll 
 .ext: 
      mov [esi],dl
      mov eax,[lpOutBuf]
      cmp ecx,0
      je .ex
  .l: dec esi
      mov dl,[esi]
      mov [eax],dl
      inc  eax
      dec  ecx
      cmp  ecx,0
      jne .l
      mov  dl,0
      mov  [eax],dl
 .ex:     pop   esi ecx edx esi ebx
return
section '.idata' import data readable writeable
     _library kernel32,'KERNEL32.DLL',\
         user32,'USER32.DLL';,\
     include 'C:\FASM135\147\INCLUDE\apia\kernel32.inc'
     include 'C:\FASM135\147\INCLUDE\apia\user32.inc'
section '.edata' export data readable
  export 'dll2.dll',Wind,'Wind'
section '.reloc' fixups data discardable
Первые строки format PE GUI 4.0 DLL и entry DllEntryPoint указывают компилятору создать РЕ-файл динамической библиотеки с точкой входа на метке DllEntryPoint.

Как Вы могли заметить, динамическая библиотека практически ни чем не отличается от обычной программы Windows, кроме того, что входная процедура вызывается системой в четырёх случаях:
  • Когда динамическая библиотека загружается в адресное пространство процесса (параметр fdwReason = DLL_PROCESS_ATTACH = 1);
  • Когда динамическая библиотека выгружается из адресного пространства процесса (параметр fdwReason = DLL_PROCESS_ DETACH = 0);
  • Когда создается новый поток в рамках динамической библиотеки (параметр fdwReason = DLL_ THREAD_ATTACН = 2);
  • Когда разрушается (прекращается) поток в рамках динамической библиотеки (параметр fdwReason = DLL_ THREAD_DETACH = 3).
В обработке стартовой процедуры мы определяем значение параметра fdwReason и если динамическая библиотека была загружена или выгружена выдаём соответствующее сообщение.

В конце файла мы создаём секцию экспорта нашей библиотеки:
Assembler
1
2
3
section '.edata' export data readable
  export 'dll2.dll',Wind,'Wind'
section '.reloc' fixups data discardable
В таблице мы задаём имя файла динамической библиотеки и имена экспортируемых процедур.

Макрос export определён следующим образом:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
macro export dllname,[label,string]
{ common
    local module,addresses,names,ordinal,count
    count = 0
   forward
    count = count+1
   common
    dd 0,0,0,rva module,1
    dd count,count,rva addresses,rva names,rva ordinal
    addresses:
   forward
    dd rva label
   common
    names:
   forward
    local name
    dd rva name
   common
    ordinal: count = 0
   forward
    dw count
    count = count+1
   common
    module db dllname,0
   forward
    name db string,0 }
Сперва, объявляются локальные метки и подсчитывается количество экспортируемых процедур:
Assembler
1
2
3
4
5
6
  common
    local module,addresses,names,ordinal,count
    count = 0
   forward
    count = count+1
   common
Затем заносится смещение RVA имени библиотеки:
Assembler
1
    dd 0,0,0,rva module,1
Следующая строка заносит общее количество экспортируемых функций, количество функций экспортируемых по имени (при использовании этого макроса все функции экспортируются по имени), смещение начала массива смещений функций, смещение начала массива смещений имен экспортируемых функций и смещение массива смещений ординалов функций.
Assembler
1
    dd count,count,rva addresses,rva names,rva ordinal
Далее объявляется метка начала массива смещений экспортируемых функций и создаётся массив их смещения:
Assembler
1
2
3
addresses:
   forward
    dd rva label
Следом объявляется метка начала массива смещений имён экспортируемых функций и создаётся сам массив:
Assembler
1
2
3
4
5
common
    names:
   forward
    local name
    dd rva name
Затем объявляется метка начала массива смещений ординалов функций и создаётся массив ординалов экспортируемых функций:
Assembler
1
2
3
4
5
common
    ordinal: count = 0
   forward
    dw count
    count = count+1
Ну и в конце макроса записывается имя динамической библиотеки и создаётся массив имен экспортируемых функций:
Assembler
1
2
3
4
common
    module db dllname,0
   forward
    name db string,0
Для более подробного ознакомления со структурой секции экспорта необходимо читать специальную литературу по структуре РЕ - файлов.

Теперь давайте посмотрим, на программу вызывающую нашу динамическую библиотеку:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
format PE GUI 4.0
entry start
include 'include\kernel.inc'
include 'include\user.inc'
include 'include\macro\stdcall.inc'
include 'include\macro\import.inc'
section '.data' data readable writeable
  Avtor db 'Автор:Пороткин Алексей Сергеевич',0
  podskazka db 'Функция Wind возвратила указатель на строку:',0
section '.code' code readable executable
  start:
   invoke Wind
   invoke MessageBox,0,eax,podskazka,MB_OK
   invoke ExitProcess,0
section '.idata' import data readable writeable
  library kernel,'KERNEL32.DLL',\
      user,'USER32.DLL',\
      MyDll,'C:\FASM135\mp\Dll2\Dll2.DLL'
  kernel:
  import     ExitProcess,'ExitProcess'
  user:
  import     MessageBox,'MessageBoxA'
 MyDll:
  import Wind,'Wind'
Обычное графическое приложение не имеющее своего окна и ресурсов размером 2 кБ. Импортирует только три функции две предоставляемые системой Windows и одна написанной нами динамической библиотекой. Как видите подключение созданной нами динамической библиотеки, ничем не отличается от подключения стандартных библиотек Windows. В этом и заключается вся прелесть использования FASM'а, он не берёт на себя слишком много, что в купе с гибким макроязыком позволяет сделать очень многое максимально просто.
0
Ушел с форума
Автор FAQ
 Аватар для Mikl___
16371 / 7683 / 1080
Регистрация: 11.11.2010
Сообщений: 13,757
18.08.2015, 08:58  [ТС]
Формат исполняемых файлов MS COFF (obj - файлы)

Использование obj-файлов в FASM, лично мне, не кажется жизненно необходимым, однако использование этого формата возможно понадобится при создании проектов на С/С++. Именно на таком примере мы и рассмотрим формат файлов MS COFF.

Для примера напишем программу, выводящую MessageBox с текущей датой. Модуль на ассемблере будет составлять строку символов, а модуль на С++ будет выводить эту строку в MessageBox.

Вот модуль на ассемблере:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
format MS COFF
;public _WinMainCRTStartup
public _outs
public ?Wind@@YAPADXZ
extrn '__imp__GetLocalTime@4' as GetLocalTime:dword
include 'include\kernel.inc'
include 'include\macro\stdcall.inc'
include 'c:\fasm135\include\lib\strings.mac'
include 'c:\fasm135\include\lib\strings.asm'
section '.text' code readable executable
 ; _WinMainCRTStartup:
proc ?Wind@@YAPADXZ
    enter
    push    ebx esi edi
    invoke   GetLocalTime,Date
    mov      ebx,_outs
    StringCopyHTML ebx,day
    add      ebx,eax
    xor      eax,eax
    mov      ax,[Date.wDay]
    stdcall  IntToString,h4,eax
    StringCopyHTML ebx,h4
    add      ebx,eax
    StringCopyHTML ebx,mon
    add      ebx,eax
    xor      eax,eax
    mov      ax,[Date.wMonth]
    stdcall  IntToString,h4,eax
    StringCopyHTML ebx,h4
    add      ebx,eax
          StringCopyHTML ebx,year
    add      ebx,eax
    xor      eax,eax
    mov      ax,[Date.wYear]
    stdcall  IntToString,h4,eax
    StringCopyHTML ebx,h4
    add      ebx,eax
    mov      eax,_outs
    pop edi esi ebx
    return
section '.data' data readable writeable
  h4    db 'Сообщение',0
  Date SYSTEMTIME
  day db 'День:',0
  mon db ' Месяц:',0
  year db ' Год:',0
  _outs: times 30 db 0
Обратите внимание на строку 2, где закомментировано определение точки входа в программу (_WinMainCRTStartup)если эту строку раскомментировать, то точка входа в программу будет именно в модуле на ассемблере. Результат работы функции записывается в переменную _outs и адрес (указатель на строку) возвращаем в регистре еах. В этом модуле используются макросы копирования строки в строку (StringCopyHTML ebx,day). Этот макрос не входит в поставку компилятора и содержится в файлах strings.mac и strings.asm он просто копирует строку (не включая нулевой символ), указатель на которую передаётся вторым параметром, в строку, указатель которой передаётся вторым параметром, после его выполнения в регистре еах количество скопированных символов. Эта процедура выдаёт строку типа:
Code
1
"День: 28 Месяц: 10 Год:2003"
Для использования объектного модуля созданного на ассемблере необходимо в проект на С++ добавить созданный нами объектный файл (Projecthttps://www.cyberforum.ru/cgi-bin/latex.cgi?\toAdd to projecthttps://www.cyberforum.ru/cgi-bin/latex.cgi?\toFiles. . . в появившемся окне необходимо выбрать тип файлов obj и указать используемый файл). Теперь рассмотрим текст программы на MS Visual C++ 5.0:
Assembler
1
2
3
4
5
6
7
8
9
#include «windows.extern  char *  __cdecl Wind(void);
LPSTR cmsk,dat;
 
int PASCAL WinMain(HANDLE   hInstance,HANDLE hPrevInstance,LPSTR lpszCmdParam,int nCmdShow)
{
    dat=Wind();
    MessageBox(0,dat,dat,MB_OK);
}
Здесь ничего сложного: подключаем файл «windows.h», затем объявляем внешнюю процедуру, возвращающую указатель на строку символов с нулём в конце, объявляем переменную dat, как указатель на строку символов, и наконец функция WinMain, в которой вызываем нашу функцию (Wind) на ассемблере, и выводим результат в MessageBox. Внимательный читатель скажет: "Стоп, но в модуле на ассемблере функцию мы объявили как? ?Wind@@YAPADXZ, а в модуле на С++ вызываем Wind ?". Всё правильно. Просто MS Visual C++ 5.0, к объявленным внешним (находящимся в скомпилированных модулях) процедурам добавляет дополнительные символы, для контроля соответствия типов передаваемых результатов из процедуры и переменных в процедуру. Вот краткая структура имени функций формируемых MS Visual C++:
Префикс
Объявленное
имя переменной
Разделитель
Тип вызова
процедуры
Тип
результата
Тип
параметров
Постфикс
?Wind@@YAPADXZ
Причём постфикс изменяется в зависимости от наличия передаваемых в функцию параметров. Если параметры передаются, то постфикс "@Z", а если параметры не передаются то просто "Z".

Типы вызова процедуры следующие:
ТипОбозначение
__cdecl A
__fastcall I
__stdcall G
Типы результата и передаваемых переменных имеют одинаковые обозначения:
Тип результатаРазмерОбозначение
char 1 D
unsigned char 1 E
short 2 F
unsigned short 2 G
enum 2 ?AW4__unnamed@@
long 4 J
unsigned long 4 K
int 4 H
unsigned int 4 I
float 4 M
double 8 N
long double 10 O
bool 1 _N
Причём если передаётся (получается) не параметр, а его адрес (например char *) то перед обозначением переменной добавляется PA. Если параметры, получаемые из процедуры или передаваемые в процедуру, отсутствуют, вместо них ставится X.

Читатель может сказать: "Это как же нужно мозг морщить, чтобы всё запомнить и не перепутать ничего при составлении имени процедуры?". Такая работа имеет основное свойство: она не особо интеллектуальна, но муторна и действия при её решении вполне укладываются в рамках алгоритма. Отсюда вывод: компьютер на то и создан, что бы выполнять подобные задачи так пусть сам, и добавляет все эти префиксы, постфиксы и т.п. Напишем макрос, который будет объявлять процедуру составляя её имя из имени которое мы ему передаём и добавляя к нему все необходимые префиксы.

Использовать переменные длиннее 4 байт мы не будем, но думаю Вы сами сможете модернизировать макросы под свои нужды если понадобится.

Для начала мы должны определить символьные константы, соответствующие типам вызова процедуры и типам получаемых и возвращаемых параметров:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
; константы типов вызовов процедур
__cdecl fix A 
__fastcall fix I
__stdcall fix G
; константы типов переменных 
void fix X
char$ fix PAD
char  fix D
unsigned_char$ fix PAE
unsigned_char fix E
short$ fix PAF
short fix F
unsigned_short$ fix PAG
unsigned_short fix G
long$ fix PAJ
long fix J
unsigned_long$ fix PAK
unsigned_long fix K
int$ fix PAH
int fix H
unsigned_int$ fix PAI
unsigned_int fix I
float$ fix PAM
float fix M
Далее следует определение макроса:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
macro exp_proc calltype,returns,name,[arg,type]
{
dr fix
forward
 dr fix dr#type
 common
if type eq void
public ?#name#@@Y#calltype#returns#dr#Z
?#name#@@Y#calltype#returns#dr#Z:
else 
public ?#name#@@Y#calltype#returns#dr#@Z
?#name#@@Y#calltype#returns#dr#@Z:
end if
common
 virtual at ebp+8
    if ~ arg eq
   forward
     local ..arg
     ..arg dd ?
     arg equ ..arg
   common
     end if
     ..ret = $ - (ebp+8)
    end virtual
    local ..dynamic_data,..dynamic_size
    dynamic_data equ ..dynamic_data
    dynamic_size equ ..dynamic_size
    virtual at ebp - dynamic_size
     dynamic_data:
}
В первой строке мы очищаем временную символьную константу, далее следует указание препроцессору обработать строку "dr fix dr#type" для всех значений параметров type начиная с первого. Эта строчка, просто создаёт константу содержащую все символы соответствующие всем получаемым процедурой параметрам. Например если в процедуру передаётся два параметра типа int, то dr будет эквивалентно HH. Далее следует указание препроцессору следующие строки обработать только один раз (common) в этих строках мы делаем видимой из других модулей программы метку (название) нашей процедуры и собственно следует содержимое макроса proc, рассмотренного ранее.

Полагаю, этот макрос не должен вызвать затруднений. Сложности в понимании может вызвать только строка "?#name#@@Y#calltype#returns#dr#@Z", объясню её более подробно. Сперва стоит знак вопроса (посмотрите табличку со структурой имени процедуры), после которого, значок номера (#), он говорит препроцессору, что далее следует символьная константа, которую следует склеить в одну метку с предыдущим текстом. Если константы с указанным именем не существует, то она включается, как есть. Рассмотрим последовательность действий препроцессора. Метка состоит из вопросительного знака, затем (без разделителей) добавляется, передаваемая макросу переменная name, после неё добавляется текст @@Y, после текста добавляется передаваемая макросу переменная calltype, затем переменная returns, потом составленная нами константа dr, и наконец постфикс @Z (или просто Z, если в процедуру не передаются параметры).

Всё. Макрос создан. Однако это ещё не всё. С таким определением процедуры не всегда можно использовать стандартные макросы enter и return так как тип вызова __cdecl предполагает, что стек после завершения работы процедуры будет очищаться вызывающей процедурой то есть модулем написанным на С++, а стандартный макрос return сам очищает стек. Вот макрос, содержащий эпилог процедуры для типа вызова __cdecl:
Assembler
1
2
3
4
5
6
7
macro cdecl_ret
    {
      sub   ebp,dynamic_size
      mov     esp,ebp
      pop     ebp
      ret
    }
Но не только эпилог нужно изменить, но и пролог тоже:
Assembler
1
2
3
4
5
6
7
8
macro cdecl_enter
    {
      dynamic_size = $ - dynamic_data
        end virtual
        push  ebp
        mov   ebp,esp
        add   ebp,dynamic_size
    }
Полагаю эти два коротеньких макроса не вызовут затруднений.

Ну и пример использования данных макросов:
Assembler
1
2
3
4
5
6
7
8
exp_proc __cdecl,char$,Wi,first,int,second,int
 cdecl_enter
   push ebx esi edi
   mov    eax,[first]
   stdcall IntToString, _outs,eax
   mov    eax,_outs
   pop  edi esi ebx
 cdecl_ret
Функция IntToString преобразует 32-х битное без знаковое число (второй параметр) в строку (адрес строки передаётся первым параметром).

Модуль на С++ будет выглядеть следующим образом:
Assembler
1
2
3
4
5
6
7
8
9
10
11
#include 
extern  char * __cdecl Wind(void);
extern  char * __cdecl Wi(int c,int b);
LPSTR cmsk,dat;
int PASCAL WinMain(HANDLE   hInstance,HANDLE   hPrevInstance,LPSTR    lpszCmdParam,int      nCmdShow)
{
    dat=Wi(12,30);
    MessageBox(0,dat,dat,MB_OK);
    Wind();
MessageBox(0,dat,dat,MB_OK);
}
Думаю больше пояснений не нужно.


Формат исполняемых файлов MS DOS

Формат исполняемых файлов MZ

Программирование под DOS в FASM в принципе почти ничем не отличается от программирования под DOS в любом другом компиляторе.

Вот простейший пример программы:
Assembler
1
2
3
4
5
6
7
8
9
format MZ
    push    cs
    pop ds
    mov ah,9
    mov dx,hello
    int 21h
    mov ax,4C00h
    int 21h
hello db 'Hello world!',24h
Как видите ничего сложного.


Формат исполняемых файлов СОМ

Если директива format не указана создаются файлы формата *.сом. Вот пример:
Assembler
1
2
3
4
5
6
7
org 100h        ;Указание загрузить программу начиная с адреса 100h
use16       ;Создавать 16-ти битный код
        mov ah,9
        mov dx,hello
        int 21h
        int 20h
hello db 13,10,'Привет всем$'
Думаю здесь всё понятно.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
18.08.2015, 08:58
Помогаю со студенческими работами здесь

Посчитать количество вхождений подстроки S1,на языке FASM
Дана строка S.Посчитать количество вхождений подстроки S1,на языке FASM.

Программирование SIMD библиотек на Fasm в x86-64 Linux
Начал недавно проект по разработке SIMD бибилиотек для С++ на Fasm под 64-bit Linux. Интересно услышать мнение матерых программеров как о...

Работа с циклами на языке Assembler
При решении системы y=a/(x+1) , x < -1 и y=3-x , x >= -1 на отрезке с шагом p вылезает ошибка 0xC0000095: Integer overflow на делении....

Организация циклов в языке Assembler
Помогите пожалуйста с лабораторной. Компилятор fasm. Дана фраза. Выяснить, сколько раз встречается ситуация, когда последняя буква...

Написать на языке Assembler пример: (2*a+3*b)/(a-b)
Как написать на языке Assembler пример: (2*a+3*b)/(a-b)


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
4
Ответ Создать тему
Новые блоги и статьи
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
Фото: Daniel Greenwood
kumehtar 13.11.2025
Расскажи мне о Мире, бродяга
kumehtar 12.11.2025
— Расскажи мне о Мире, бродяга, Ты же видел моря и метели. Как сменялись короны и стяги, Как эпохи стрелою летели. - Этот мир — это крылья и горы, Снег и пламя, любовь и тревоги, И бескрайние. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru