Comments 86
Неплохо, но Вы ничегошеньки не сказали о настоящих шедеврах: архитектуре и ассемблерах от DEC (ярчайшие звезды PDP-8 и PDP-11). Все, что было после них - убожество в смысле простоты, логичности, изящества. Если что - никого обидеть не хочу
По той же причине не рассказал и про PIC например :) как видите, я постарался выбрать несколько архитектур которые более-менее в ходу у программистов и самодельщиков и по сей день - так чтобы был практический смысл. Про антикварные, архаичные и страшные системы наверное можно отдельную статью написать именно для любителей поностальгировать.
Ну, у них тоже свои недостатки имелись. И не всё там просто, логично и изящно. Скажем, что в PDP-11 логичного в том, что у XOR один из операндов должен быть в регистре, в то время как у остальных простых команд он может находиться и в памяти? Или почему сложение и вычитание (ADD, SUB) только словные, а остальные операции -- и словные, и байтовые? Или почему сложение и вычитание с переносом (ADC, SBC) имеют только один операнд, т.е. нельзя сложить сразу два операнда и добавить к ним перенос? Или почему нет операции "И", есть только сброс битов, отмеченных единицами, а не нулями во втором операнде (BIC)? А если вспомнить про тамошний FPU, то почему аккумуляторов всего шесть, причём только четыре могут использоваться в любых командах? (Объясняется почти всё, конечно, элементарно: банально не лезли все команды в 16-разрядное слово, их кодирующее, поэтому и пришлось чем-то жертвовать -- но простоты, логичности и изящности это точно не добавляет.)
Правда, если сравнивать PDP-11 с 8086, то там вообще невозможно понять, какие такие вещества употребляли архитекторы Интела :)
я думаю можно проще сказать - сравнивая ассемблеры RISC-архитектур (хотя бы те же ARM и AVR) c архитектурами CISC (x86 и 8051) мы сразу замечаем что первые как будто проще и логичнее вторых... но это собственно и следует из первой буквы аббревиатур (риск/циск) - усечённая или сложная система команд.
PDP-11 -- CISC, IBMовская Система 360 -- тоже CISC, но они пологичней будут, скажем, ARMа (и особенно -- если сравнивать с системой команд Thumb, а не с классической ARMовской), особенно PDPшка... Просто и 8086 с его наследниками, и 8051 -- интеловские архитектуры, а значит, изначально отвратительны; их с чем ни сравнивай -- всё ужасом будут выглядеть (особенно если погрузиться в детали, а не глянуть лишь краем глаза)
В PDP было все вполне логично: двух операндые и однооперандные команды разбивались на фиксированные поля (размер данных, регистр источника\приемника, методы адресациии).И ограничения некоторых команд связаны с ограниченностью базового поля кода полноценной двухоперандной команды - для него оставалось всего 3 бита из 16. Для некоторых коменд пришлось жертвовать другими полями. По этой же причине например в MSP430 пришлось ввести отдельную команду PUSH (POP реализован через MOV).
А по x86 - это да. Я "сломал мозг" когда дошел до их префиксов , читая книжку с низкоуровневым описанием комнад x86 (386).
возможно дело в том кто с чего начинал :) я понял насколько x86 ассемблер мудрёный только когда позже стал писать под AVR
А как я дико плевался от 8086/8088, до этого поработав с PDP-11, System 360 (ну, с их советскими аналогами, есно), а также с 6502 и 8080 :) Правда, от последнего я тоже плевался -- но 8-разрядному процу ещё как-то прощаешь некоторые вещи. Впрочем, 6502 тоже 8-разрядный, но в тыщу раз приятней (а заодно и быстрей работает, несмотря на в 2-3 раза меньшую тактовую частоту).
Не согласен. Ассемблер 8080, а тем более 8086 логичный, удобный и понятный.
В школе на уроках программирования был класс из ДВК и БК0010Ш. Писал на них прямо в машинных кодах, ибо это просто сказка как удобно и легко запоминается все. Бывало только с таблицей команд сверялся по каким нибудь не часто используемым условным переходам. Таскал оттуда БКашку домой на каникулы :) с учителем были отличные отношения. Со второго урока мне были вручены ключи от кабинета с компами и сказано - иди, тебе тут на теории делать нечего.
вот любопытно что вам казалось удобно и легко - а коллега выше в комментариях вспоминает архитектуру PDP-11 (я так понимаю БК0010 производная от него) как довольно мудрёную :)
прекрасный показатель того что на вкус и цвет ассемблеры по-разному воспринимаются - но кстати тоже соглашусь что после преодоления первых 10-20 минут шока от непривычности синтаксиса и погружения в архитектуру - дальше уже чувствуешь себя довольно комфортно - не много запоминать требуется :)
Любая архитектура ограничена во-первых, законами физики, а во-вторых, технологическими возможностями эпохи. Законы физики, что тогда (60 лет назад), что сегодня одни и те же. А вот технологические возможности - несопоставимы (взять хотя бы память: сегодня это микросхемы на гигабайты, а тогда - ферритовые кольца на килобайты). Инженеры DEC создали пусть и не идеальные, но действительно изящные архитектуры. Обидно то, что развитие технологии никак не коррелировало с архитектурой. Технологии развивались, а архитектура - деградировала
Нормально было у DEC с развитием архитектур. Они своевременно делали переходы на VAX и потом Alpha. Просто они долго "забивали" на рынок ПК. А потом было уже поздно.
Альфа оказалась, скажем так, невостребованной. А что до ПК, они пытались в начале 1980-х сделать нечто вроде ПК с системой команд PDP-11 -- что было совершенно правильно, -- но сделали её несовместимой по железу с настоящими PDPшками, чтобы, не дай Бог, злые пользователи не могли воспользоваться штатными ОС (и программами) от минимашин. Если же учесть стоимость всего этого дела, не приходится удивляться, что оно не взлетело -- хотя технически было в 100500 раз совершенней ублюдской IBM PC. Правильным было бы делать именно что полноценную PDPшку, только компактную и под "персональное" использование, но с любыми штатными ОС, прочим ПО и потенциально (с помощью некоего адаптера) с возможностью подключать любую старую периферию. И, естественно, предлагать это за разумные деньги (вплоть до того, что в начале продаж торговать в убыток). Но жадность фраера сгубила (с)
Да, БК и ДВК это классический PDP-11. Может быть это было просто все очень ново для меня тогда и все казалось зеленее и выше чем сейчас :) но маш коды загрузились в голову на ура. На ассемблере как таковом я не писал, компилятора не нашел в классе. Был только Бейсик. До БК немого писал на КР580ВМ80А который наш аналог 8080. Но там был не полноценный комп, а что-то типа самопального Альтаира со светодиодиками и переключателями. Но там маш коды использовал по таблице команд. Такого уровня как с PDP11 не было.
Кстати, в порядке придирок. И БК, и ДВК-1, и ДВК-2 в плане системы команд -- не PDP-11, а LSI-11 (обрезанная PDPшка). Ну а по периферии БК точно не является даже LSIшкой, там только проц совместимый. Насчёт ДВК в этом плане я не в курсе.
Это вполне возможно, читал то наши советские документы на эту технику, там могло быть и не совсем корректно или полно все отражено. Но про LSI-11 даже и не слышал, а PDP-11 с тех лет помню.
Асмы были идентичнв. Делал игруху на двк со спрайтами и легко перенес на бк. Многие так и делали, тк дисковод был и редакторы приличные на двк
В современное время кросс-компиляторы существуют для старых машин например на КМ1801ВМ2 ? Или демо/софт пишут вручную?
чаще просто можно старый софт найти и запустить в эмуляторе
Не могу сказать. После школы больше не возвращался к этой платформе.
Ну, если бы мне понадобилось, я бы взял эмулятор полноценной PDPшки, запустил бы на нём полноценную RSX-11M, и в ней бы уже писал. Т.е., по сути, делал бы так же, как 30+ лет назад :)
В древних версиях GCC была поддержка PDP-11, но её, насколько знаю, давным-давно выпилили, поскольку поддерживать некому.
У TI Eclipse для его MSP430 как-то компилирует из C. И Energia IDE для MSP430. Значит что-то современное есть.
MSP430 != PDP-11
Нашел давно забытую страницу 2004 года, где сделано сравнение:
Summary
In summary, the MSP-430 instruction set designers sacrificed orthogonality in its addressing modes in order to support a larger number of registers. Several useful tricks were applied in order to save the situation, but those they managed to implement also served to increase the specialized nature of addressing modes in the MSP-430 and those they failed to implement (PC relative calls, for example, which must be emulated) can only leave one wondering what they were thinking about.The MSP-430 reserves an entire bit in the instruction to select between word and byte modes. But the PDP-11 was also able to support byte and word mode accesses, where needed, as well as a full complement of instructions, including integer multiplication and division and a set of floating point opcodes. The PDP-11 also uses a symmetrical form for the addressing modes which extends to both operands in two-operand instructions. (The PDP-11 also balanced the range field for branch offsets with the need for a variety of opcodes - something I won't discuss in detail.)
The MSP-430 designers made a pivotal choice (misguided, in my opinion) for 16 registers and in the process found themselves making sacrifices, even while retaining the more important addressing modes and adding some creative adaptations within the limitations they accepted in order to provide some modest but attractive compensations (e.g., the constant generator.)
I like the MSP-430 a lot, despite these warts and problems. Frankly, the hardware is impressive and the MCU remains excellent.
https://web.archive.org/web/20070208052109/http://users.easystreet.com/jkirwan/new/msp430.html
Просто есть разные стороны "мудрёности". Кодирование команд в PDP-11 -- очень простое и понятное; мой первый комментарий касался уж очень восторженной её оценки, и я просто показал, что там свои проблемы тоже имеются. Но, если брать по совокупности, PDP-11 -- лучшая из 16-разрядных систем команд всего мира, пожалуй.
И да, я до сих пор часть кодов команд помню :) Правда, в кодах мне писать надобности не было, работая на полноценных СМках.
В 1989 начал проект на 8035(мл. брат 8051) контроллере. Так получилось, что доступа к ЭВМ(тогда мы еще этим словом пользовались) не было. Поэтому писал весь код в кодах (объем примерно 1.5 кБ).
Было весело. Особенно когда сдохла батарейка на энерго-независимом электронном диске (набор РУ10 с интерфейсом для СМ1800). Пришлось набирать код заново.
мне кажется от тех времен осталось ещё немало "учебных макетов" на которых процессор (типа 8085), плата, цифровой индикатор и 16-ричная клавиатура - тоже чтобы в кодах набирать. ну и Клайв Синклер до ZX ведь такую штуку для хоббиистов изобрёл и продавал - за 40 фунтов (наверное как 1000 баксов сейчас) - желающие покупали такое чудо с 256 байтами оперативки.

но безусловно любопытно услышать "из первых рук" историю человека кто делал реальный проект в таком режиме :)
Да, я тоже как-то делал приборчик на нашем аналоге 35го. Там стояла кроватка для 27Cxx, но там уже я на ассемблере писал и заливал это "Стерхом" программатором. Но было ужасно неудобно стирать каждый раз пачку УФэшек и был создан аналог 27с из РУ10, напаяв на него по питанию большой кондер и пару резисторов на разрешение записи и на включение. Хватало чтобы зашить на Стерхе и воткнуть в прибор и включить.
Было весело. Особенно когда сдохла батарейка на энерго-независимом электронном диске (набор РУ10 с интерфейсом для СМ1800). Пришлось набирать код заново.
Если Вы программировали на тетрадном листочке,то проблем разумеется не возникло. Мне приходилось перенабирать текст десятки (если не сотни) раз, потому что завесить любую машину в то время было плёвое дело.
ИМХО - MSP430 имеет ассемблер неотличимый от DEC.
у меня слишком скромный опыт в ассемблерах от DEC (т.е. опять же для PDP о чем коллеги выше упоминали) - но инторнеты подтверждают ваше наблюдение :) вот прямо вбиваю запрос в гугле в духе "MSP430 assembly instructions resemble PDP" и сразу горстка ссылок где прямо говорится что TI по-видимому нарочно взяли похожую систему команд - и изобретать меньше - и пользователям может оказаться привычнее
Ещё как отличимый. Хотя параллели действительно имеются, и наверняка создатели MSP430 именно PDPшками вдохновлялись.
Дай обниму тебя, брат!
Pdp-11 просто и понятно. После него x86 казался нелогичным.
Моторола 56ххх с еë тремя командами на строку , парой аккумуляторов и парой шин данных тоже довольно неплохая штука
в "современном исполнении"
В современном - это всё таки 64 бита и явная инструкция sycall. А приведённый код уже скорее ностальгию вызывает )
возможно поскольку я сильно погряз в ARM-овских микроконтроллерах для меня 32-бита до сих пор современность :) хотя замечание насчет syscall точно валидное - допишем его в этот параграф
Ну, для микроконтроллеров 64 бита смысла просто не имеет: нет там задач, которые не полезут в 4 Гбайта адресного пространства. Более того, 80% микроконтроллерных задач и на 8-разрядном можно сделать, просто, учитывая стоимость МК, проще уже для всех проектов использовать одну и ту же архитектуру, чем скакать между разными.
микроконтроллеры разные бывают, я почти уверен что в области DSP что-нибудь найдётся :) вот почти уверен что на сайте TI или Аnalog Devices (Аналоговые Девицы!) - что-то найдётся
Ну, 64-разр микропроцессоры, может, и найдутся, а вот микроконтроллеры... Сомнительно-с
там ведь размыта дифференциация - вот PIC64 я открываю спецификацию - они обозначаются как MPU а не MCU - но при этом читаю:
Peripheral Interfaces
• Up to four TSN Ethernet endpoint ports supporting rates
from 10M to 10G
• Two SPI, four UART, four I2C, 64 GPIO, two MDIO, JTAG
host, timers and watchdogs
для микропроцессора это довольно необычно :)
Ну, сейчас сплошь SoC (системы на кристалле), хотя граница уже давно стала размываться -- ещё с 1980-х, на самом-то деле. Лично я делю по простому принципу: если можно собрать работающую плату, не используя какую-либо внешнюю память (ни ОЗУ, ни ПЗУ -- в том числе в виде, скажем, SD-карты), то это микроконтроллер, если же внешняя память нужна -- микропроцессор.
мне тоже кажется удобной такая дифференциация, но ранние микроконтроллеры (например 8051) не имели внутренней памяти ведь - и даже более модерновые могут не иметь встроенного генератора частоты например. или скажем младшие AtTiny - у них ни встроенной ОЗУ ни внешней :)
ну в общем да, сошлись на том что границы расплылись...
Ну, большинство ранних внутреннюю память имели: скажем, 64 или 128 байт оперативы и 1 или 2 Кбайта УФ ПЗУ. Хотя, вроде, действительно были требующие для работы внешнего ПЗУ, но уж не помню, честно говоря: я имел дело лишь с 8051, очень немного и лишь с вариантом с памятью на борту, всё остальное -- только микро- (или не микро :) ) процессоры. Это сейчас почти исключительно с микроконтроллерами работаю
Совсем без внутренней памяти это 8031. 8051 имели внутреннюю память программ.
Требование наличия встроенного (П)ПЗУ из этого простого принципа лучше убрать. В него например микроконтролеры ESP плохо вписываются.
А у ESP32S2 (с другими вообще никаких дел не имел) достаточно просто и хитро одновременно: сам по себе это микропроцессор, у него нет флэша на борту. Однако в корпусе микросхемы лежит отдельный кристалл флэша, и они внутри соединены. Т.е. для пользователя это выглядит как микроконтроллер, хотя технически это две отдельных микросхемы в одном корпусе. В общем, про что и говорил выше: граница размыта
Главное отличие микроконтроллера - отсутствие аппаратного менеджмента памяти (MMU), что затрудняет портирование на них многозадачных ОС с загрузкой пользовательских программ.
Для тех, кто почти ничего не знает об ассемблерах (у меня совсем мало таких знаний) — бомба, живо написанный туториал. Негативные комментарии в основном от тех, кто и так машинный код пишет и машинным маслом запивает.
А где же наш горячо любимый RISC-V ? Хотя бы RV32I.
очевидно до них у меня пока руки не дошли - ну что ж, займёмся! тема действительно насущная в последние пару лет они явные конкуренты stm32 - только по-моему uart-загрузчика из коробки нет, возможно поэтому я пока с ними тормозил :)
Подушню малость: RISC-V -- архитектура, а STM32 -- микроконтроллеры. Корректней говорить, что RISC-V -- конкурент для ARM (архитектура против архитектуры).
ну да, по названиям всё так - но о конкуренции архитектур говорить сложно, архитектуры сами себе пользователям не продаются :) а вот всплывшие в последнее время контроллеры от WCH, подозрительно схожие с STM32 по форм-фактору, но в целом предлагающие бОльшие ресурсы за меньшие деньги - это прямая конкуренция. (т.е. речь о конкретно CH32V203 например в сравнении с ST32F103 или соседними)
правда с точки зрения писания кода, конечно, мы точно будем говорить о сравнении системы команд... но сначала надо дойти до магазина, распаять какую-нибудь платку и научиться компилить и прошивать... с информацией пока не фонтан :(
Была статья на Хабре про CH, под которой была ссылка на сообщество разработчиков на этих контроллеров https://t.me/ch32v
Судя по названию, CH32V203 -- скорей, конкурент STM32F2, а не F1. Но от вряд ли конкурент для, скажем, STM32H7 или для какого-нибудь STM32F0. Разные ресурсы = разные задачи, а какая конкретно процессорная архитектура -- уже не столь важно, если говорить именно о моделях МК.
Почему авторы статей базового уровня по ассемблеру x86 так любят реальный режим и DOS?
По-моему, эта комбинация — одна из самых неприятных и отталкивающих. У многих вообще формируется порочная ассоциативная связь: я как-то общался с одним человеком, который работал преподавателем программирования в колледже — когда я сказал, что, в числе прочего, пишу код на ассемблере, он скривил лицо и сказал что-то вроде «фу, ассемблер это же какой-то допотопный язык времен ДОСа». То, что любой современный исполняемый файл состоит из таких же машинных команд, которые можно записать в виде человеко-читаемого ассемблерного листинга, в его картину мира не укладывалось.
Виноваты в этом статьи про ассемблер, которые все дружно начинают знакомство читателя с ассемблером приводя в пример 16-битный реальный режим, 16-битные регистры и прерывания DOS/BIOS.
честно говоря не очень понятно кому это адресовано - если мне - то статья отнюдь не начинается с доса и им не заканчивается. тут акцент выраженно на ассемблер для контроллеров (как по мне - наиболее адекватная сфера для него в наше время)
Не важно, что бОльшая часть посвящена ассемблерам для МК-шных архитектур. Вопрос в том, что как только вы коснулись x86-ассемблера, то сразу всплывают
mov ah, 9
int 21h
Во-первых, это создаёт ощущение того, что вы пересказываете статьи 30-летней давности, для которых такие сниппеты-примеры были актуальными. Хотя как раз вот такое подобное API (между прикладным софтом и ДОС-ом и между гипотетической ОС реального режима и БИОС-ом) морально устарело, а вот сам x86-ассемблер актуален как никогда, покуда жива x86-архитектура, пусть и в виде x86-64.
Во-вторых, это чисто эстетически не самый приятный и разумный способ организации API. Он исторически сложился, но не стоит начинать знакомство а написанием кода на ассемблере именно с демонстрации такого подхода. Почему бы в качестве примера не привести что-то вроде
push ...
push ...
push ...
push ...
call [MessageBoxA]
Это и актуально, и не так запутанно, и универсально, и пример можно скомпилировать и запустить как под Windows 95, так и под современной виндой. А потом уже в качестве шок-контента показать, как делалось взаимодействие с системными API в стародавние времена.
В-третьих, даже если закрыть глаза на неактуальность DOS-прерываний в реальном режиме, а представить, что это супер-актуально и что ничего другого нет — опять же, именно такой код в таком виде подаёт дурной пример. И за этим делом стоит, на самом деле, большой философский вопрос. Да, я знаю, что именно так (int 10h) в большинстве своём все и пишут (или писали). Но правильно ли так писать?
Подумайте вот над каким глубоким вопросом: что самое главное в ассемблере, что первостепеннее всего в ассемблере и почему ассемблер вообще называется ассемблером (а не мнемо-транслятором или машкод-транслятором или чем-то таким)?
Когда я был юным и глупым, я бы уверенно ответил, что в первую очередь ассемблер это инструмент, который транслирует инструкции для процессора, написанные в человеко-читаемом виде (в виде текста из мнемоник инструкций и операндов) в машинный код в его непосредственном виде. То есть я бы сказал, что написать свой ассемблер — значит написать трансялятор, который для каждой строчки вроде xor eax, eax
или nop
или int 10h
выплюнет в ответ байты 33С0
, 90
или CD10
. Но прошли годы, мне довелось и свой собственный x86-ассемблер написать, и написать just-for-fun реализацию компилятора Си , и я могу с уверенностью сказать, что трансляция текстового представления инструкций в бинарное представление это вообще ерунда и абсолютно тривиальная задача, а самое главное в ассемблере совсем не это.
А что же тогда? Тут стоит задаться вопросом: вот есть ассемблер для x86, для MIPS, для ARM, для AVR, а может ли существовать ассемблер вообще ни под какую архитектуру, то есть ассемблер, вообще не знакомый ни с одной из архитектур, не знающий ни одной машинной команды? Будет ли хоть какой-то смысл существования такого инструмента? Тогда можно поставить вопрос иначе: могу ли я взять какой-нибудь x86-ассемблер (например FASM, MASM, NASM) и с его помощью породить на свет машинный код для AVR8? Ведь набор инструкций совсем другой, а даже если какие-то мнемоники и совпадают, опкоды совсем другие и принцип кодирования операндов тоже совсем другой. На самом деле — могу, если просто проведу трансляцию текстового представления машинных инструкций в бинарное и в своём ассемблерном листинге запишу всё в виде директив .db. Нечестная игра? Очень тяжело? На самом деле, не очень-то и тяжело — написано один раз и работает всегда. А есть в ассемблерах (для любых архитектур) кое что ещё, без чего писать код, править и дорабатывать его было бы на порядок или на несколько порядков более тяжело, чем если бы конвертировать инструкции в бинарный вид пришлось бы вручную.
Это всевозможные директивы процессора, это возможность объявлять константы, это возможность ставить метки, это возможность в коде и в данных (записываемых в виде других директив) ставить упоминания этих меток, это способность ассемблера самостоятельно вычислять абсолютные адреса и относительные адреса или смещения в нужных местах, это возможность в своём коде указать не просто метку, но и сделать арифметику с метками и константами и заставить ассемблер посчитать корректный адрес или смещение одной сущности относительно другой.
Вот это самое главное (по крайней мере я так считаю) в ассемблерах и самое сложное при написании своего ассемблера для какой бы то ни было архитектуры.
Почему сложное? Что здесь сложного?
А вот, например, что. Возьмём для простоты архитектуру x86. С точки зрения человеко-читаемого представления кода всё очень легко:
jmp <метка1>
или
jge <метка2>
С точки же зрения машинного кода как для безусловного джампа так и для всех условных джампов существует две версии: одна версия инструкции после которой процессор ожидает увидеть относительное смещение в виде 8-битного знакового числа, в другой версии смещение будет закодировано уже в виде 32-битного или 16-битного знакового числа (в зависимости от того, в 32-битном или 16-битном режиме выполняется задача, код которой выполняет процессор + к этому к инструкции может быть примерён префикс 66h переопределяющий размер адреса на противоположный).
Так вот, если мы пишем свой собственный x86-ассемблер, который идёт по строкам, перебирая одну за другой, и для каждой новой инструкцией делает трансляцию в машинный код, то здесь возникает проблема. Как ассемблеру обработать машинную команду вида jmp some_label? Выбрать для этого короткий форму кодирования с опкодом EB или длинную форму с опкодом E9? Предположим, мы генерируем код для 32-битного режима. Тогда инструкция с опкодом EB будет занимать 2 байта, а инструкция с опкодом E9 — 5 байт, из которых четыре это относительный адрес места назначения прыжка. Можно было бы поступать тупо и везде для всех джампов, как безусловных так и условных, использовать длинный вариант инструкции. Но это бы безмерно разувало машинный код, потому что ветвлений в коде чуть ли половина от всего объёма команд.
Можно поступать умнее: если jump destination лежит относительно недалеко и смещение относительно адреса следующей инструкции лежит в пределах от –128 до +127, то нужно использовать компактный двухбайтовый вариант инструкции, в противном случае — длинный трёх или пятибайтовый.
Проблема, однако, в том, что если встречаем в джампе упоминание метки, до которой мы ещё не добрались (т.е. джамп вперёд, а не назад), то мы пока ещё не знаем расстояние до неё и не знаем, укладывается ли это расстояние/смещение в вышеупомянутый диапазон. А значит мы не знаем, как получится длина инструкции, которую мы прямо сейчас генерируем.
Что мы тут можем сделать как авторы гипотетического ассемблера? Мы можем рекурсивно запустить ассемблирование (точнее трансляцию) далее следующих инструкций с указанием вложенноу рекурсивному вызову делать её до тез пор, пока не доберёмся до нужной метки — по возврату мы уже будем знать точное расстояние до интересующей нас метки, сможем выбрать между коротким джампом и длинным джампом, и, сгенерировав его, продолжим трансляцию с того места, до которого добрался только что сделанный рекурсивный вызов. Либо мы можем в условиях неопределённости применять всегда длинный вариант джамп-инструкции, но заносить все такие инструкции в особый список, а по завершению первого прохода, когда взаимное расположение всех меток станет окончательно ясным — провести второй проход и пройтись по всем джамп-инструкциям из списка, и те их них, где можно было бы обойтись коротким вариантом вместо длинного, заменить на короткий вариант.
Проблема в том, что ни первый ни второй подход не работают. Если метка лежит впереди и джамп ведёт вперёд и мы не знаем, какое до него расстояние и у нас возникает неопределённость относительно длины текущей транслируемой инструкции, мы не можем тупо взять и прямо сейчас попробовать ассемблировать/транслировать последующие инструкции (с целью определить величину смещения), либо отложенным проходом расставить все точки над i, потому внутри этого прыжка могут оказаться другие джамп-инструкции с неопределённой длиной, а у тех, в свою очередь, destination-метка может быть выбрана так, что уже длина тех инструкций тоже будет в состоянии неопределённости и попадёт в зависимость от длины транслируемой в данной момент джамп-инструкции. Иными словами: длины джамп-инструкций зависят от того, насколько далеко нужно прыгать, а если перепрыгивать нужно через другие джамп-инструкции, которые тоже в свою очередь непонятно какой длины в конечном итоге получатся, то зависимость между длинами джамп-инструкций может стать циркулярной, замкнутой. И тогда в первом подходе мы получаем бесконечную рекурсию, а во втором подходе мы получаем бесконечный цикл на втором проходе (компактификации длинных джампов), потому что компактификатор будет заменять длинный джамп на короткий в одном конкретной месте, а маленькое изменение будет приводить к сдвигу всех (или многих) последующих инструкций на несколько байт назад, сдвиг куска машинного кода на несколько байт переведёт к необходимости пересчёта кучи других смещений, стоящих в разных местах кода, и где-то в результате такого пересчёта величина смещения увеличится настолько, что короткий вариант джамп-инструкции перестанет переходить, а это в свою очередь вызовет необходимость поменять короткий джамп на длинный, а это вызовет новый сдвиг точек/меток, новый пересчёт смещений, то есть это запускает цепную реакцию, конца у которой может не быть.
Конечно, и от этого можно защититься, если для каждого такого места, которое мы собираемся перекраивать в плане замены длины инструкций в пользу большей компактности, ввести флаг или счётчик попыток перекроить инструкцию: если мы сгенерировали сначала длинный джамп (но занесли его в особый список, чтобы потом попробовать применить компактный), потом заменили на компактный (чем вызвали пересчёт всех, а точнее многих прочих меток и смещений), и оказалось, что это вызвало волну пересчёта смещений и вынудило нас где-то в другом месте поменять короткий джамп на длинный, что в свою очередь вынуждает нас опять первоначальный джамп поменять на короткий, то не нужно бесконечно менять короткую вариацию инструкции на длинну и наоборот — уже после второй попытки стоит ставить крест и оставлять длинную вариацию инструкции.
Тогда, конечно, ассемблер не зависнет в процессе ассемблирования, но такой наивный подход к решению проблемы не даёт гарантии, что мы сгенерировали оптимальный в плане размера код: возможно мы отказались заменить одну джамп-инструкцию с длинной на короткую, но в результате 50 других джамп-инструкций, которые могли бы существовать в короткой форме, пришлось оставить в длинной вариации.
Смысл этого опуса в том, что процесс вычисления меток, их взаимного расположения в некоем адресному пространстве, вычисления смещения между ними и текущей точкой ассемблирования/трансляции и подбор нужного варианта кодирования одной и той же инструкции (но с разной длиной, которая в конечном итоге зависит от расположения меток друго относительно друга) — это очень нетривиальный процесс, по сравнению с которым тупое транслирование текстового представления в бинарный машинный код становится супер-тривиальной задачей.
Тогда смысл ассемблера, который вообще ни в курсе ни про одну архитектуру и не знает ни одной мнемоники очень простой — с помощью такого ассемблера по прежнему можно создать код машинный код под любых архитектуру, просто забивая опкоды и кодируя операнды ручками, но при этом вычислять и подставлять адреса и относительные смещения за нас будет инструмент. Нам не придётся от одной правки где-то в середине кода пересчитывать десяток адресов/смещений и менять их в коде. С другой стороны, если такой столь странный инструмент умеет работать с метками и директива, мы можем с помощью такого ассемблера сформировать какой-нибудь не исполняемый, но при этом бинарный файл со сложной структурой. Какой-нибудь PDF, или x509-сертификат, или MBR с таблицей разделов или что-то ещё табличное и с древовидной структурой. Нам не придётся вручную считать смещения, адреса и индексы, за нас это сделает ассемблер.
К слову, наличие таких директив, меток и адресной арифметики с ним даёт нам возможность на выходе ассемблера получить, например, PE-файл или ELF-файл корректного формата даже в том случае, если ассемблер вообще ни сном ни духом ни про PE-формат, ни про ELF-формат и не поддерживает из коробки генерацию исполняемых файлов в таком формате, а может выдавать на выходе только raw-бинарники. Разве что с полем типа CRC/checksum будет проблема — его вычисления распространными директивами типа .org/.align и адресной арифметикой на добьёшься.
Так вот, к чему я об этом всём? Во-первых, как мне показалось, вы мало внимания уделяете обзору именно этой стороны ассемблера: всем этим директивам, константам и трюкам с адресной арифметикой. А это, между прочим, присуще в той или иной степени всем ассемблерам, и с другой стороны, жизнь без именно этих фишек была бы адом — гораздо большим адом, чем если бы int 10h пришлось бы превращать в .db CDh, 10h вручную, вооружившись табличкой. Хотя у вас пример с применением этой адресной арифметики в коде проскальзывает (len = . - msg)
, но я вном виде об этом в тексте статьи упоминания нигде нет.
Ну а потом, это собственно, то с чего я начал третью претензию. Раз вы не собираетесь делать секрет из того, что помимо собственно трансляции машинных команд ассемблеры понимают ещё и директивы, объявления констант, какие-то ассемблеры понимают макросы, почему вы собственно не пользуетесь этими константами?
Почему mov AH, 9? Почему int 20h? Почему вы не объявляете константы для прерываний, функций и подфункий, не даёте им внятные человеко-читаемые имена? Вам не кажется, что эти 9 и 20h это то, что отталкивает от мысли освоить ассемблер, потому что пугает необходимостью зазубривать все это номера прерываний и функций?
Почему mov AH, 9? Почему int 20h?
потому что статья - краткий обзор а не учебник от Питера Нортона с демонстрацией создания шестнадцатеричного редактора для дисков в объёме 200 страниц.
цели статьи указаны в самом начале. я не сторонник тюториалов-разжевалок - считаю что важно показать направление, возможности - а дальше кто заинтересовался - сам уж всё найдёт
однако что мешает Вам исправить ситуацию и написать собственную статью? в частности использовать бОльшую часть данного комментария как отправную точку.
потому что статья - краткий обзор а не учебник от Питера Нортона с демонстрацией создания шестнадцатеричного редактора для дисков в объёме 200 страниц.
Никто не спорит, но приводя пример ARM-кода вы всё же используете константы GPIO0DIR
и GPIO0DATA
вместо магических чисел 0x50008000
и 0x50003FFC
, в примере с AVR8 вы используете константы PORTD
и DDRB
вместо магических 0x12
и 0x17
и так далее, но именно в случае x86 почему-то используются магические числа вместо констант с внятными именами. Вот именно к этой непоследовательности и вопрос.
однако что мешает Вам исправить ситуацию и написать собственную статью? в частности использовать бОльшую часть данного комментария как отправную точку.
Мешает гнетущение ощущение, что такая статья окажется никому не нужной и не интересной, недостаток времени и почти что неспособность бороться с тенденцией «и тут Остапа понесло» при написании статей (и комментариев тоже, но с комментариев спрос не такой строгий и ответственность не такая сильная).
что такая статья окажется никому не нужной ... недостаток времени
уж комментарий-то точно гораздо меньше людей увидят (а тем более прочтут) так что в сравнении с ним статья гораздо нужнее :) и раз уж на него времени хватило то можно попытаться
(хотя в целом, между нами, я сам здорово сомневаюсь в нужности статей на хабре - и вообще в рунете - так, кое-какие мысли решил в кучку собрать и записать - м.б. чтобы потом перевести и переиспользовать в каком-то персональном блоге)
уж комментарий-то точно гораздо меньше людей увидят (а тем более прочтут) так что в сравнении с ним статья гораздо нужнее :) и раз уж на него времени хватило то можно попытаться
Комментарий я обычно строчу даже не перечитывая. А статья это 300 итераций с перепрочтением, перестановкой абзацев, укорочением одних предложений, уточнением других, рисованием картинок.
Какой офигенный комментарий. Прям на отдельную статью тянет, с удовольствием бы почитал.
Хотел плюсик в карму поставить, а он там стоит уже(
вот, значит не мне одному показалось что надо в статью :)
@firehacker- слышите?
Спасибо. Зачастую нужен волшебный пинок, да.
Собственно, тов. firehacker правильно говорит про то, что асм - это не только (и не столько) про опкоды, а про метки, секции, расчёт адресов и т.д. Кстати в разделе про gas и linux это и показано - что ассемблер создаёт ОБЬЕКТНЫЙ ФАЙЛ, который потом ЛИНКУЕТСЯ в исполняемый файл. То же самое можно было добавить и в разделе про DOS и TASM, ибо сам по себе tasm тоже не создаст исп. файл, к нему в довесок нужен TLINK. А то, что современные ассемблеры типа FASM/NASM - это не только ассемблеры, но и линковщики заодно (выходной формат таргет-файла выбирается директивами/опциями командной строки) - ЭТО ДРУГОЕ...
Наверное потому, что для многих запустить dosbox проще чем as. ;)
А препод, такое говорящий, -- идиот. Хотя бы потому, что ассемблер -- это не "допотопный язык времён ДОСа", а "естественный" язык для любой машины независимо от времени и ОС. Впрочем, кто может работать -- работает, а кто работать не может -- тот учит (с)
а что, где-то кто-то сказал про допотопный?
вообще немного забавно когда ассемблер рассматривают как самый нижний уровень на который можно спуститься в разработке. в то время как энтузиасты программируемой логики воплощают собственные процессоры на VHDL
VHDL, как и Verilog, -- не язык программирования, а язык описания аппаратуры, и сходство только внешнее. Так что ниже ассемблера в программировании опуститься нельзя (машинный код не считаем, ибо ассемблер -- его прямое отражение, только удобочитаемое), ниже -- разработка аппаратуры, а это уже таки другое.
ну тут видимо кому что - я электронщик исторически и для меня аппаратура это не полностью "другое". отчасти такой взгляд оправдан - иногда думая "да пачиму разработчики системы команд такой страх сочинили" нужно вспомнить что система команд не рождается из ничего - она воплощена таки в "этих ваших транзисторах" (ну или скорее логических блоках) и порой физические/технические ограничения находят отражение на нашем уже программном (низком) уровне
Воплощение в транзисторах не помешало DEC создать удобную систему машинных команд, читаемых прямо в коде, даже не в ассемблере.
back in the late 70s my software colleagues always said that the PDP-11,
M6800, M6809, M68000 were all designed by SOFTWARE engineers.It was clear to us that the Intel 8008, 8080, 8088, 8086 etc were designed
by HARDWARE engineers ... just take a look at their architectures,
instruction sets and opcode mnemonics!
ёмко и кратко отсюда
Вполне может быть. "Потребителем" процессора является программист-ассемблерщик, так сказать -- соответственно, ТЗ должно исходить от него, а не от электронищков, которые будут собственно разработку железа производить. Ну, понятно, это сильно упрощая, но идея такая, да.
а не от электронищков, которые будут собственно разработку железа производить
ну, это очень узкий взгляд на вещи :) в этом деле много коньюнктуры - цена процессора на выходе и предполагаемый объем продажи не на последнем месте. и имеющаяся технологическая база естественно.
Сейчас основной потребитель ISA - разработчик компилятора и требования у него по сравнению с человеком, пишущим на ассемблере вручную, несколько другие.
Не совсем "естественный". Естественный язык - это машинные коды, которые зачастую тяжелы для восприятия. Ассемблер - это самый простой язык перевода с человеческого на машинный и обратно.
А как же NASM и FASM в качестве ассемблеров х86?! Не говоря уже о бессмертных MASM и Debug...
Во времена когда переходил с win98 на mandrake привычный мне tasm естественно ожидал облом, поплевавшись с at&t синтаксисом gas я пошел на радио рынок и нашел там дискету с fasm, там были исходники, которые с первого раза собрались под linux, поковырявшись там с системой макросов я ускорил время разработки драйверов раза в три, с тех пор прошло много времени, драйвера уже давно не так критично шпарить на асме, но от fasm остались исключительно теплые воспоминания
Ассемблеры, 5 штук — быстрое знакомство для тех кто не знаком