Историческая причина: Вы работаете на машине, для которой требуется включить загрузочный код на передней панели. (И да, это было сделано. Регулярно в первой паре поколений машин.)
Не то, что вы искали, по современной причине: Когда вы пишете ассемблер, вы должны понимать процесс.
Действительно крутой пример - это знаменитый полиглот, который, помимо всего прочего, является действительным DOS .COM файлом, потому что ASCII в его исходном коде удваивается как двоичные x86 инструкции! http://ideology.com.au/polyglot/polyglot.txt
Более скучные примеры...
Многие процессоры реализуют инструкции ISA как последовательности более примитивных микроинструкций (по сути, наборов управляющих сигналов датапата), которые "микрокодируются" в ПЗУ микрокода.
Для достаточно простого процессора вы можете написать микрокод непосредственно в двоичном формате, а не собирать его из мнемонического языка. Или, если вы занимаетесь реинжинирингом процессора, вы можете не знать его набор микроинструкций и просто должны догадаться о формате микроинструкций... в этом случае вы, вероятно, также будете работать в двоичном формате. В любом случае это более низкий уровень, чем язык ассемблера.
Иногда код для старых процессоров, таких как 6502, использовал недокументированные инструкции, которые не имели официальных мнемоник, поэтому вам приходилось писать двоичные значения, а не инструкции ассемблера.
Если вы создаете переводчика. Возможно, у вас доработан интерпретатор, но не парсер. Вы можете протестировать интерпретатор, написав программу, которую нужно интерпретировать, в чистом двоичном формате.
Помнится, я читал, что Воз написал первый Apple BASIC (Apple I? Apple II?) на машинном языке. До того как у них появились запоминающие устройства, вам нужно было вводить шестнадцатеричные коды в монитор.
Генерация динамического кода:
Если у вас есть очень простая проблема, которую нужно решить, и производительность важна, часто бывает хорошей идеей проанализировать проблему - space и на лету сгенерируйте специализированную функцию для решения проблемы.
Один практический пример: высокопроизводительная математика с разреженными матрицами.
Это часто связано с умножением массивов чисел от тысяч до миллионов раз. Поскольку многие элементы матрицы могут быть равны нулю или единице, вы можете значительно сэкономить время, если удалите все тривиальные умножения.
Для этого небольшой генератор кода может анализировать матрицы и генерировать машинный код для матричной арифметики на лету. Это может варьироваться от использования библиотеки JIT (или встроенной языковой функции) до очень простых схем.
Для случая умножения разреженных матриц вы можете получить отличную производительность, просто склеив вместе готовые фрагменты кода для разных случаев. Это можно сделать в 50 строках C-кода.
Когда я тренировался во время службы на флоте (примерно в 1986 году), у нас был компьютер, который нам дали, чтобы научиться устранять неисправности электроники, а не программировать устранение неисправностей, который был запрограммирован путем ввода двоичной информации в переднюю часть компьютера, и мы должны были сказать инструктору, что они сломали в машине, основываясь на результатах, а также на устранении неполадок оборудования. Насколько мне известно, одна из этих машин все еще может быть поблизости.
Я хотел бы найти для него свой исходный код, я на самом деле написал симулятор машины и компиляцию языка для машины. Поразительно, сколько работы можно было проделать с 1024 байтами памяти! :)
Даже если вы пропустите ассемблер и перейдете сразу к машинному коду, вы будете использовать не двоичный, а шестнадцатеричный код.
В школе мне приходилось исправлять код в памяти с помощью отладчика без помощи ассемблера. Несмотря на свою занимательность, этот навык не имеет практически никакой ценности за пределами отладки встроенных систем.
Также учтите, что мнемоники опкодов, используемые в ассемблере, должны иметь соответствие 1:1 с реальными опкодами (отсюда и термин "мнемоника"), поэтому, набирая машинный код вручную, вы не сможете сделать ничего такого, чего не смогли бы сделать на ассемблере. Роль ассемблера заключается в преобразовании мнемоник в опкоды (также определяя, какая версия конкретной инструкции должна быть использована - например, немедленный или косвенный MOV), меток в адреса и другие подобные задачи.
Полезно знать, что происходит внутри ассемблера, но это почти никогда не пригодится, если только вы не ищете ошибку в ассемблере, не взламываете встроенный гаджет или не выкручиваетесь из очень, очень странной ситуации.
Для студенческого проекта мне пришлось разработать упрощенный микроконтроллер на VHDL. (язык описания оборудования). Чтобы проверить это, я написал чрезвычайно простую программу в двоичном формате, потому что это был наиболее удобный способ передать программу в моделируемый микроконтроллер.
Раньше нередко приходилось переходить с двоичного кода на ассемблер, чтобы понять дамп.
Но не использовать ассемблер? Я не могу придумать ни одной причины. Ассемблер - это уже программирование на голом железе. Единственная польза от него - это возможность использовать обозначения типа "add" для фактической (двоичной) инструкции. и т.д.
У меня не было никакого ассемблера к моему восьмибитному Atari, поэтому я писал машинный код напрямую. Чтобы запустить код из BASIC, вы либо записываете код в виде десятичных байтов данных, либо в виде строки. (Да, вы действительно могли писать код в строке, единственный символьный код из 256, который вы не могли набрать, был 155 - код возврата. К счастью, в машинном коде 6502 нет инструкции с таким значением, так что это было проблемой, только когда ветка оказывалась на 101 байт назад (-101 = 155).)
Я до сих пор помню обычный кусок кода для запуска таймера:
104 (pla)
169, 7 (lda #7)
162, 6 (ldx #6)
160, 10 (ldy #10)
76, 92, 228 (jmp 0xE45C)
В последние годы я участвовал в некоторых ассемблерных соревнованиях по оптимизации размера. Несмотря на то, что большая часть кода - это ассемблер, вы все равно должны точно знать, какие инструкции производит ассемблер, чтобы знать, сколько в них байт. Кроме того, иногда используются такие трюки, как использование некоторых байтов в качестве данных и кода, или некоторые байты являются разными инструкциями в зависимости от того, вводите ли вы первый байт или вводите его в середине инструкции. Тогда вы записываете инструкции как байты данных в середине ассемблерного кода.
Несколько раз вы получаете выгоду от работы с необработанным машинным кодом, а не только с языком ассемблера. Например, рассмотрите возможность отправки двоичного файла по электронной почте, но с помощью почтовой программы, которая не знала, как декодировать вложения. В свое время несколько человек написали небольшие программы, которые могли декодировать остальную часть вложения, но все в программе было печатным символом. Итак, вы расшифровываете вложение, сохраняете тело письма как something.com
, а затем выполняете его. Он расшифрует вложение и напишет двоичный файл, который вы затем сможете выполнить.
В качестве другого примера, несколько лет назад на Фидонете возникла довольно простая задача: написать программу, которая просто печатает число, которое увеличивается при каждом запуске, но (часть, которая усложняет задачу) не разрешается использовать какие-либо внешние файлы или другое хранилище для работы. Чтобы это не стало слишком скучным, это тоже была своего рода игра кода, хотя измеренный размер был исполняемыми байтами, а не исходным кодом. Довольно много записей для этой задачи использовали самомодифицирующийся код, который сильно зависел от того, как именно были закодированы инструкции и тому подобное.
Подождав секунду, я вижу, что у меня все еще есть исходный код одной из моих попыток:
.model tiny,c
.286
.code
.startup
main proc
mov si,offset count
inc byte ptr [si]
mov al, [si]
mov bx,4090h
shr al, 4
call convert
lodsb
and al,0fh
mov byte ptr end_convert, 08bh
convert:
add al,bl
daa
adc al,bh
daa
int 29h
end_convert:
ret
db 0d6h
; mov dx, si
mov ah,3ch
xor cx, cx
int 21h
xchg bx, ax
mov dx,offset main
mov cx,offset the_end - offset main
int 21h
ret
main endp
count:
db 0
name:
db 'c.com', 0
the_end:
end
Мне лучше уйти сейчас, прежде чем я буду нести ответственность за кого-то, у кого будет апоплексический приступ (надеюсь, я не слишком поздно ...)
Вы поняли - если нет [dis]ассемблера. Я бывал в ситуациях взлома прошивок, когда тратил достаточно времени на просмотр необработанных потоков инструкций PowerPC, чтобы быть в состоянии распознать и собрать вручную несколько типов инструкций. (В итоге я портировал дизассемблер: http://homepage.mac.com/potswa/source/DisDave.sit, если вам удастся его установить.)
Некоторые ISA намного проще других. RISC следуют простым форматам, и в них легко ориентироваться, потому что инструкции обычно одинаковой длины и выровнены по границам слов. x86-64, с другой стороны, полна кодировок переменной длины и префиксных кодов.
В проектах FPGA или при создании собственных схем очень часто разрабатывается некий поток инструкций и вручную кодируется в двоичном формате.
Ну, вы могли бы использовать hex для программирования некоторых базовых инструкций загрузки в ОЗУ или ПЗУ вместо использования ассемблера, если бы вы были разработчиком микросхемы. Я так и сделал для написанной мной софтинки.
Реально, после того, как вы это сделаете, следующим шагом будет написание базового ассемблера на Perl или чем-то подобном.
В пост-апокалиптическом мире, где все клавиатуры и мониторы были уничтожены, и единственный способ запрограммировать Тетрис в ваш компьютер - это переключатели на передней панели, да.
Но если серьезно, зачем кому-то делать такое?
Edit: очевидно, есть люди, разрабатывающие процессоры, которые должны программировать в двоичном формате, пока они не смогут запустить ассемблер на своих процессорах, но это очень маленькая группа людей.
Когда вы вручную взламываете двоичные форматы, как это делает A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux.
Еще в 1997 году я делал это на калькуляторах TI-83, когда учился в школе и не имел доступа к соединительному кабелю.
Обычно в то время вы просто пишете ассемблерную программу, используете TASM для ее сборки, а затем передаете ее на калькулятор через соединительный кабель. Но если мне было скучно и я хотел собрать что-то маленькое, я запомнил достаточно байтовых инструкций, чтобы иметь возможность вводить их для определенных вещей.
Боковое примечание Конечно, это было забавно, если в программе была ошибка, потому что это могло легко повредить всю оперативную память калькулятора.Таким образом, вам придется удерживать кнопку ON и / или извлекать батарейки AAA и надеяться, что этого было достаточно для восстановления вычислений (без каких-либо программ, которые были в памяти). В противном случае для аппаратного сброса пришлось бы отверткой открутить специальный резервный аккумулятор. Хорошие времена ...