Мова праграмавання нізкага ўзроўню

З Вікіпедыі, свабоднай энцыклапедыі

Нізкаўзроўневая мова праграмавання — гэта мова праграмавання, якая практычна не абстрагуецца ад архітэктуры набору інструкцый камп’ютара: каманды ці функцыі ў моўнай карце структурна падобныя з інструкцыямі працэсара. Звычайна гэта адносіцца альбо да машыннага кода, альбо да мовы асэмблера. З-за нізкай (адсюль і слова) абстракцыі паміж мовай і машыннай мовай мовы нізкага ўзроўню часам называюць «блізкімі да апаратуры». Праграмы, напісаныя на нізкаўзроўневых мовах, як правіла, адносна не партатыўныя, паколькі аптымізаваны пад пэўны тып сістэмнай архітэктуры[1].

Мовы нізкага ўзроўню могуць пераўтварацца ў машынны код без кампілятара ці інтэрпрэтатара — у мовах праграмавання другога пакалення выкарыстоўваецца прасцейшы працэсар, званы асэмблерам. У выніку код выконваецца непасрэдна на працэсары. Праграма, напісаная на мове нізкага ўзроўню, можа выконвацца вельмі хутка і займаць мала месца ў памяці. Аналагічная праграма на мове высокага ўзроўню можа быць менш эфектыўнай і займаць больш памяці. Мовы нізкага ўзроўню простыя, але лічацца складанымі ў выкарыстанні з прычыны вялікай колькасці тэхнічных дэталяў, якія праграміст павінен памятаць. Для параўнання, мова праграмавання высокага ўзроўня ізалюе семантыку выканання архітэктуры камп’ютара ад спецыфікацыі праграмы, што спрашчае распрацоўку[1].

Машынны код[правіць | правіць зыходнік]

Пярэдняя панэль мінікамп’ютара PDP-8/E. Шэраг пераключальнікаў унізе можа быць скарыстаны для пераключэння ў праграме на машыннай мове.

Машынны код — гэта адзіная мова, якую камп’ютар можа апрацоўваць наўпрост, без папярэдняга пераўтварання. У цяперашні час праграмісты практычна ніколі не пішуць праграмы непасрэдна на машынным кодзе, паколькі гэта патрабуе ўвагі да шматлікіх дэталяў, якія мова высокага ўзроўню апрацоўвае аўтаматычна[1]. Акрамя таго, ён патрабуе запамінання ці пошуку лічбавых кодаў для кожнай інструкцыі і вельмі складаны для мадыфікацыі.

Сапраўдны машынны код уяўляе сабой паток неапрацаваных, як правіла, бінарных даных. Праграміст машыннага кода звычайна праграмуе інструкцыі і даныя ў больш чытэльнай форме, напрыклад у дзесятковай, васьмярковай ці шаснаццатковай сістэме, якія перакладаюцца ва ўнутраны фармат праграмай, званай загрузчыкам, ці ўводзяцца ў памяць камп’ютара з пярэдней панэлі[1].

Хоць толькі нешматлікія праграмы пішуцца на машыннай мове, праграмісты часта набываюць навык яго чытання, працуючы з дампамі ядра ці адладжваючы яго з пярэдней панэлі.

Прыклад функцыі ў шаснаццатковым уяўленні 32-разраднага машыннага кода x86 для вылічэння n-ай лічбы Фібаначы:

8B542408 83FA0077 06B80000 0000C383
FA027706 B8010000 00C353BB 01000000
B9010000 008D0419 83FA0376 078BD989
C14AEBF1 5BC3

Мова асэмблера[правіць | правіць зыходнік]

Мовы другога пакалення забяспечваюць адзін узровень абстракцыі па-над машынным кодам. На світанку кадавання на такіх камп’ютарах, як TX-0 і PDP-1, першае, што рабілі хакеры з Масачусецкага тэхналагічнага інстытута — гэта пісалі асэмблеры. Мова асэмблера мае мала семантыкі і фармальных спецыфікацый, з’яўляючыся адлюстраваннем чалавекачытальных сімвалаў, уключаючы сімвальныя адрасы, кады аперацый, адрасы, лічбавыя канстанты, радкі і г.д. Як правіла, адна машынная інструкцыя прадстаўлена адным радком асэмблернага кода. Асэмблеры ствараюць аб’ектныя файлы, якія могуць звязвацца з іншымі аб’ектнымі файламі ці загружацца самастойна.

Большасць асэмблераў прадстаўляюць макрасы для генерацыі агульных паслядоўнасцяў інструкцый.

Прыклад: Той жа калькулятар лічбы Фібаначы, што і вышэй, але на мове асэмблера x86-64 з выкарыстаннем сінтаксісу AT&T:

_fib:
    movl $1, %eax
    xorl %ebx, %ebx
.fib_loop:
    cmpl $1, %edi
    jbe .fib_done
    movl %eax, %ecx
    addl %ebx, %eax
    movl %ecx, %ebx
    subl $1, %edi
    jmp .fib_loop
.fib_done:
    ret

У гэтым прыкладзе кода рэгістры працэсара x86-64 названы і маніпулююцца непасрэдна. Функцыя загружае свае ўваходныя даныя з %edi ў адпаведнасці з System V ABI і выконвае разлік, маніпулюючы значэннямі ў рэгістрах EAX, EBX і ECX, пакуль не скончыцца і не вернецца. Звярніце ўвагу, што ў гэтай мове асэмблера няма канцэпцыі вяртання значэння. Калі вынік быў захаваны ў рэгістры EAX, каманда RET проста перамяшчае апрацоўку кода ў месца, якое захоўваецца ў стэку (звычайна інструкцыя адразу пасля той, якая выклікала гэту функцыю), і гэта залежыць ад аўтара кода выкліку. ведаць, што гэтая функцыя захоўвае свой вынік у EAX і можа атрымаць яго адтуль. Мова асэмблера x86-64 не навязвае стандарт для вяртання значэнняў з функцыі (і фактычна не мае паняцця функцыі); код выкліку павінен праверыць стан пасля вяртання працэдуры, калі яму трэба атрымаць значэнне.

Параўнайце з той жа функцыяй на :

unsigned int fib(unsigned int n) {
  if (!n)
    return 0;
  else if (n <= 2)
    return 1;
  else {
    unsigned int a, c;
    for (a = c = 1; ; --n) {
      c += a;
      if (n <= 2) return c;
      a = c - a;
    }
  }
}

Нізкаўзроўневае праграмаванне ў мовах высокага ўзроўню[правіць | правіць зыходнік]

У канцы 1960-х гадоў мовы высокага ўзроўню, такія як PL/S, BLISS, BCPL, пашыраны ALGOL (для вялікіх сістэм Burroughs) і C, прадугледжвалі пэўную ступень доступу да функцый нізкаўзроўневага праграмавання. Адным з метадаў такога доступу з’яўляецца ўбудаваны асэмблер, пры якім асэмблерны код убудоўваецца ў мову высокага ўзроўню, які падтрымлівае гэтую магчымасць. Некаторыя з гэтых моў таксама дазваляюць выкарыстоўваць архітэктурна-залежныя дырэктывы аптымізацыі кампілятара, каб наладзіць кампілятар на выкарыстанне архітэктуры мэтавага працэсара.

Зноскі

  1. а б в г 3.1: Structure of low-level programs (англ.). Workforce LibreTexts (5 сакавіка 2021). Праверана 3 красавіка 2023.