Недавно мне показали эмуляторы ассемблера для 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
Демонстрация по ссылке.
Заключение
Есть еще несколько инструкций загрузки данных, работы с оперативной памятью и памятью кода. Помимо них оффлайновый эмулятор имеет пару «магических» вызовов которые позволяют читать-писать консоль — это даёт возможность писать что-то вроде скриптов для обработки строк и т.п. — понятно, впрочем, что к оригинальным системам использовавшим данный процессор это уже не относится.
Но я вижу что уже получилось довольно много текста, хотя успела пересказать только примерно половину того что прочла. Вероятно лучше на этом остановиться и, если будет интерес, написать дополнение отдельной статьёй.
Ссылки на оригинальные инструкции:
Комментарии (32)
Mendel
30.10.2015 10:31+2Умножают, не умножают…
Помню когда впервые эту песенку услышал, и полез в вики, был удивлен что родной Z80 был таки без умножения. Хотя это единственный ассемблер который я таки знал хорошо, а не на уровне написать пару строк в паскале, для ускорения или вызова низкоуровневых вещей.
С эмуляторами тоже забавно недавно было. Знакомый сказал мол 580-й (клон 8080) он видел только в эмуляторе. Я сказал мол живьем видел, но лишь мельком, а на следующий день я нахожу у себя инструкцию по бейсику СМ1800, которая была моей первой машиной (у деда в лаборатории стояла, я собственно на этом бейсике и писал первые строчки кода). Открываю вики и вижу, что это тот самый 580...))icCE
30.10.2015 11:23+5У меня не то, что клон. У меня оригинал работает стоит :) На голове появляется седина.
AMD 8088-1hack2root
30.10.2015 11:42У меня Intel 386 работал — я его полюбил за то что это реально был прорыв в то, без чего сейчас жизнь невозможна — многозадачные многопользовательские ОС.
icCE
01.11.2015 03:10Вы не поверите!!!
80386monah_tuk
02.11.2015 03:20Пыль… Пожалейте его!
А по сути: оно ещё и работает?icCE
02.11.2015 09:39Да он вполне себе работает.
Пыли на самом деле тут не много, это он уже успел сново запылится.
вот тут у меня еще часть железок, если интересно.
fotki.yandex.ru/users/icce/album/155960
Правда не все.
icCE
02.11.2015 11:44Вот кстати нашел фото, делал пинги из DOS.
twitter.com/icce/status/616294671585775616
Mrrl
30.10.2015 13:29+1Забавная игрушка. Но для работы её, похоже, надо серьёзно изучать, а то получается ужас.
Умножение 8*8 => 16 бит. 41 байт, не больше 280 тактовfim p1,$23 fim p2,$35 fim p3,0 fim p4,0 fim p0,$08 repeat clc xch r9 ral xch r9 xch r8 ral xch r8 xch r7 ral xch r7 xch r6 ral xch r6 xch r5 ral xch r5 xch r4 ral xch r4 jcn $a,nadd clc xch r9 add r3 xch r9 xch r8 add r2 xch r8 jcn $a,nadd isz r7,nadd inc r6 nadd isz r1,repeat
Musia17
30.10.2015 13:35+1а то получается ужас
Да, ужас и есть — на сайте недавно задачу на минификацию кода э-э-э… вот она преобразовать BCD в HEX выложили — я пока и пробовать не стала. Хотя судя по тому что кто-то в 37 байт запилил — какой-то нюанс в условии, который я не вижу — а если втупую делать у меня кода на три страницы (и то не работает).Mrrl
30.10.2015 15:11С битом «C» при вычитании, конечно, засада. Логичнее, если он устанавливается в 1 при «переполнении», а не наоборот.
Похоже, что в эмуляторе неправильно работает DAA: в инструкции написано, что при C=1 шестёрка прибавляется, а в эмуляторе — не хочет, и получается 9+9=12, а не 18.
Если я правильно понял задачу, то:
35 байт, которые работают в эмулятореrepeat xch r0 dac xch r0 jcn c1,add1 ldm 9 xch r0 xch r1 dac xch r1 jcn c1,add1 ldm 9 xch r1 xch r2 dac xch r2 jcn c1,add1 ldm 9 xch r2 xch r3 dac xch r3 jcn c0,end add1 isz r8,repeat isz r9,repeat isz r10,repeat inc r11 jun repeat end
Mrrl
30.10.2015 15:2432 байта :)repeat xch r0 dac jcn c1,add0 ldm 9 xch r1 dac jcn c1,add1 ldm 9 xch r2 dac jcn c1,add2 ldm 9 xch r3 dac xch r3 jcn c0,end add2 xch r2 add1 xch r1 add0 xch r0 isz r8,repeat isz r9,repeat isz r10,repeat inc r11 jun repeat end
Musia17
30.10.2015 15:33М-м-м, по хорошему их не имеет смысла сюда постить — можно ж прям там засабмитить попытаться. А то секрет украдут :D
Mrrl
30.10.2015 15:44Действительно. Но убирать уже поздно.
А там есть что-нибудь интересное?Musia17
30.10.2015 15:51Там? Пряников и денег вроде не раздают :)
Ну просто в статистике по этой задаче можно зависнуть в топе списка назло потомкам. Но в общем на любителя конечно… Я и не пыталась эту сдавать. Но подсматривать в Ваше решение не хочу пока. М.б. прозрение озарит! Тем не менее спасибо — вернусь к нему если отчаюсь!
Loiqig
30.10.2015 14:57+1Забавная вещь, в своё время по 8080 в институтские годы нас сильно гоняли. Были стенды с самим процессором и периферией и ручным набор машинных кодов на цифровой клавиатуре. Сами написали под него компилятор, чтобы всё время по табличке не переводить, но вводить в любом случае вручную приходилось.
Спасибо, за статью и нахлынувшие тёплые воспоминания.
andy_p
03.11.2015 00:07Отсутствие умножения не значит невозможности создать сложную систему. У меня z80 решал систему нелинейных дифференциальных уравнений второго порядка (числа, естественно, были с плавающей запятой). Правда, очень медленно.
icCE
03.11.2015 11:26Ну с учетом того, что умножение — это краткая запись сложения, то действительно ничего удивительного.
Musia17
03.11.2015 14:32Да умножение-то дело нехитрое, я смутно припоминаю что умножение последовательным удвоением еще древние вавилоняне умели (т.к. они считали в 60-ричной системе и таблицы умножения запоминать было нереально).
Деление обычно самое нудное…
andy_p
> Этот дедушка не умеет не то что делить — даже умножать.
Это нормально. Z80 тоже умножать не умеет.
Musia17
Это точно :)
Между ними разницы-то — всего 5 лет. Собсно Z80 это 8080 с доп.банком регистров, как я помню из вики — а 8080 это улучшенная версия 8008. А он в свою очередь — 8-разрядная версия 4004.
Но делить даже некоторые младшие AVR не умеют как помню, хотя с ними я очень слабо знакома.
sim-dev
Делить AVR вообще все поголовно не умеют, а младшие (и старые) еще и умножать не умеют. А вот любой дедушка семейства MCS51 умеет и делить, и умножать.
Musia17
Вы правы! Спасибо! Ну я честно сказала что слаба знакома :)
tronix286
А вот у прадедушки 8048 MSC48 нет даже команды вычитания, что поначалу впечатляет.
Musia17
Ну это-то мелочи, т.к. вычитание зачастую это то же сложение. Я вот час назад разглядывая инструкцию к AVR (разбираясь с комментом выше) обнаружила что есть инструкция SUBI но нет ADDI — т.е. вычесть непосредственное значение можно а прибавить нельзя (что конечно не мешает вычесть отрицательное значение).
EasyX
mc51 — вообще отличный выбор для начинающего.
beeruser
Нет. Z80 просто обратно совместим с 8080. Помимо дополнительного банка регистров есть ещё ~500 дополнительных инструкций.
«Делить» не умеют даже совсем нескромные Itanium-ы
http://www.math.chalmers.se/Math/Grundutb/GU/MMG410/V11/FAQ/divsqrt.pdf
googol
У 8008 и у Z80 один создатель — Federico Faggin
Мне понравилась вот эта его лекция по сабжу www.youtube.com/watch?v=hugZii_eX30
beeruser
История создания Zilog и Z80
http://archive.computerhistory.org/resources/text/Oral_History/Zilog_Z80/102658073.05.01.pdf