Более реальный пример
Более реальным и интересным примером может служить пример, который позволет увидеть содержимое регистра флагов процессора:
with Interfaces; use Interfaces; with Ada.Text_IO; use Ada.Text_IO; with System.Machine_Code; use System.Machine_Code; with Ada.Characters.Latin_1; use Ada.Characters.Latin_1;
procedure Getflags is Eax : Unsigned_32; begin Asm ("pushfl" & LF & HT & -- сохранить регистр флагов в стеке "popl %%eax" & LF & HT & -- загрузить флаги из стека в регистр eax "movl %%eax, %0", -- сохранить значение флагов в переменной Outputs => Unsigned_32'Asm_Output ("=g", Eax)); Put_Line ("Flags register:" & Eax'Img); end Getflags; |
Текст этого примера может неожиданно показаться несколько сложным.
Однако это не должно вызывать большие опасения, поскольку далее будут рассмотрены все тонкости этого примера.
Следует заметить, что понимание текста этого примера позволит использовать всю мощь встроенного ассемблера GNAT.
Первое, на что необходимо обратить внимание - это способ записи множества инструкций ассемблера.
Реально, следующий пример записи множества инструкций ассемблера будет полностью корректным:
Asm ("pushfl popl %%eax movl %%eax, %0") |
В генерируемом ассемблерном файле этот фрагмент будет отображен следующим образом:
#APP pushfl popl %eax movl %eax, -40(%ebp) #NO_APP |
Не трудно заметить, что такой результат не очень легко и удобно читать.
Таким образом, удобнее записывать различные инструкции ассемблера в отдельных строчках исходного текста, заключая их в двойные кавычки, а с помощью стандартного пакета Ada.Characters.Latin_1
выполнять "ручную" вставку символов перевода строки(LineFeed / LF) и горизонтальной табуляции (Horizontal Tab / HT).
Это позволяет сделать более читабельным как исходный текст Ады:
Asm ("pushfl" & LF & HT & -- сохранить регистр флагов в стеке "popl %%eax" & LF & HT & -- загрузить флаги из стека в регистр eax "movl %%eax, %0") -- сохранить значение флагов в переменной |
так и генерируемый файл ассемблера:
#APP pushfl popl %eax movl %eax, -40(%ebp) #NO_APP |
Настоятельно рекомендуется применять данный способ для оформления исходных текстов с использованием встроенного ассемблера, поскольку далеко не все (включая и автора, спустя несколько месяцев) будут способны легко понять смысл фрагмента исходного текста на ассемблере!
При более внимательном рассмотрении исходного текста примера можно обнаружить примечательный факт наличия двух символов процента перед каждым именем регистра, хотя в генерируемом ассемблерном файле перед именами регистров присутствует только один символ процента.
Причина этого заключается в том, что при написании инструкций встроенного ассемблера, всем именам переменных и именам регистров должен предшествовать символ процента '%'.
Следовательно, поскольку ассемблер сам требует использование символа процента перед именем регистра, то мы получаем следующую инструкцию встроенного ассемблера для пересылки содержимого из регистра ax в регистр bx:
Asm ("movw %%ax, %%bx"); |
#APP movw %ax, %bx #NO_APP |
Asm ("pushfl" & LF & HT & -- сохранить регистр флагов в стеке "popl %%eax" & LF & HT & -- загрузить флаги из стека в регистр eax "movl %%eax, %0", -- сохранить значение флагов в переменной Outputs => Unsigned_32'Asm_Output ("=g", Eax)); |