Pull to refresh

Ассемблер Intel-4004 — для развлечения

Reading time4 min
Views32K
intel-4004 cpu

Недавно мне показали эмуляторы ассемблера для Intel-4004 и я на некоторое время «зависла». Это «дедушка» нынешних процессоров — если верить статье википедии — первый коммерчески распространённый микропроцессор.

Вряд ли его сейчас можно использовать для каких-то серьёзных целей. А вот просто поломать голову (вместо brainf**k) — ну, это то чем я занялась. Главное он 4-разрядный и набор команд довольно небольшой (до того я знала только немножко x86-й, по мучительному институтскому курсу).

Ниже идет вольный перевод-пересказ вот этой инструкции по ассемблеру для Intel-4004 — и краткие замечания о моих попытках написать что-нибудь на нём.



 

Сначала об Эмуляторах



Думаю, увидеть Intel-4004 живьём, а тем более в составе какого-нибудь работающего девайса в наше время непросто. Для экспериментов поэтому остаются эмуляторы:
  • e4004.szyc.org — онлайновый, кажется, единственный полноценный эмулятор всей «экосистемы», в которую входил данный процессор — содержит также ассемблер и дизассемблер, позволяет пошаговую отладку, визуализацию всего и вся — автор Maciej Szyc
  • Intel-4004 emu @ github — оффлайновый, написанный на Питоне (имеет и простенькую онлайн-версию) — но «полноценным» не является — больше для упражнений и экспериментов; онлайновая версия позволяет передавать код по ссылке, для демонстрации — я это использую ниже.


Сразу оговорюсь — я познакомилась с этой темой именно по наводке авторов второго эмулятора (и переводимая инструкция относится к нему) — но сама пробовала только его онлайновую форму, поэтому заранее приношу извинения за возможные ошибки и неточности перевода. Первый эмулятор лично мне удобнее для отладки (хотя к нему инструкция бы тоже не помешала).

 

Об Архитектуре



С точки зрения меня, как программиста, архитектура очень простая. У нас есть:

  • 4-разрядные регистры (от r0 до r15)
  • аккумулятор (тоже 4-разрядный)
  • флаг переноса (carry)
  • память кода, содержит инструкции по 1 или 2 байта
  • оперативная память — c 4-разрядными ячейками


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

Большинство логических и арифметических инструкций работают или с аккумулятором, или с ним же и одним из регистров.

 

Первая попытка



Первые две инструкции которые предлагают запомнить — ldm для загрузки числа в аккумулятор и xch — для обмена аккумулятора и регистра.

Да! У него нет инструкции для копирования аккумулятора в регистр (хотя обратная инструкция ld присутствует).

Вот программа из этих двух инструкций:

ldm 5       ; загрузим 5 в аккумулятор
xch r2      ; обменяем аккум с R2


Предлагается также посмотреть как это работает в эмуляторе по такой ссылке — нужно только нажать кнопку «Run» — и в поле Output появится содержимое регистров после выполнения кода.

 

Арифметика



Этот дедушка не умеет не то что делить — даже умножать. Как я поняла немножко поигравшись с ним, из-за этого даже перевод между 10-чной и 16-ричной системой становится очень затруднительным. Поэтому видимо предлагается считать в BCD — хранить в 4 разрядах десятичные цифры и совершать над ними специальные действия для коррекции десятичных результатов.

Сложение add — добавляет к аккумулятору заданный регистр. Кроме этого прибавляется флаг переноса (поэтому его полезно сначала сбросить с помощью clc). После сложения флаг переноса устанавливается если было переполнение.

Для счета в 10-чных числах после сложения можно выполнить инструкцию daa — по сути она, если в аккумуляторе число от 10 до 15, вычтет из него 10-ку и установит флаг переноса.

; сложим числа в R6:R7
fim r0 $67  ; загружает 6 в R0 и 7 в R1
ld r0       ; 6 пошло в аккумулятор
clc         ; очистили перенос
add r1      ; выполнили сложение, аккум теперь 13
daa         ; а теперь аккум 3 и единичка ушла в перенос

; а теперь выведем результат в R2:R3
xch r3    ; выписываем аккум в R3
ldm 0     ; и очищаем аккумулятор
ral         ; сдвигаем в него флаг переноса
xch r2   ; и выписываем в R2


Не знаю, есть ли способ попроще выписать флаг переноса в регистр. Проверить код можно по ссылке.

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

Есть инструкции инкремента iac и декремента dac для аккумулятора, отдельный инкремент inc для регистров, а также инструкции инверсии аккумулятора и переноса cma и cmc.

 

Ветвления, переходы



Для организаций конструкций вроде условий и циклов у нас есть несколько инструкций.

Безусловный переход jun — просто на заданную метку.

Вызов подпрограммы jms — точно так же, по метке (но адрес возврата заносится в стек). Возврат инструкцией bbl которая принимает число в качестве параметра — записывает его в аккумулятор. Нельзя сказать чтоб это было удобно — получается, произвольный результат вычислений в аккумуляторе вернуть нельзя.

Инструкции условного перехода умеют определять — установлен / сброшен флаг переноса, или равен / не-равен нулю аккумулятор.

jcn c0 ... ; прыжок если перенос = 0
jcn c1 ... ; прыжок если перенос = 1
jcn az ... ; прыжок если аккумулятор = 0
jcn an ... ; прыжок если аккумулятор <> 0


Есть еще интересная инструкция isz — она увеличивает заданный регистр на единицу.

Для демонстрации я напишу программу которая возводит в квадрат число, загруженное в аккумулятор:

; возводим в квадрат число из ACC, результат будет в R2:R3
ldm 5     ; загружаем в аккумулятор, например, 5

xch r0    ; скопируем число из аккума в R0
ld r0
xch r1    ; и еще в R1 - здесь будет счетчик цикла

repeat:
ld r0      ; берем наше исходное число из R0
clc
add r3   ; прибавляем его к текущему результату
xch r3   ; возвращаем младший полубайт в R3
jcn c0 nocarry
inc r2    ; а R2 инкрементируем если был перенос
nocarry:

ld r1      ; теперь уменьшим счетчик на 1
dac
jcn az finish   ; выйдем если достигли нуля
xch r1   ; возвращаем обновленное значение в счетчик
jun repeat   ; и к новой итерации цикла

finish:
nop


Демонстрация по ссылке.

 

Заключение



Есть еще несколько инструкций загрузки данных, работы с оперативной памятью и памятью кода. Помимо них оффлайновый эмулятор имеет пару «магических» вызовов которые позволяют читать-писать консоль — это даёт возможность писать что-то вроде скриптов для обработки строк и т.п. — понятно, впрочем, что к оригинальным системам использовавшим данный процессор это уже не относится.

Но я вижу что уже получилось довольно много текста, хотя успела пересказать только примерно половину того что прочла. Вероятно лучше на этом остановиться и, если будет интерес, написать дополнение отдельной статьёй.

Ссылки на оригинальные инструкции:
Tags:
Hubs:
+35
Comments34

Articles

Change theme settings