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


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

 

Заключение



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

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

Ссылки на оригинальные инструкции:

Комментарии (32)


  1. andy_p
    30.10.2015 07:54
    +6

    > Этот дедушка не умеет не то что делить — даже умножать.

    Это нормально. Z80 тоже умножать не умеет.


    1. Musia17
      30.10.2015 08:00

      Это точно :)

      Между ними разницы-то — всего 5 лет. Собсно Z80 это 8080 с доп.банком регистров, как я помню из вики — а 8080 это улучшенная версия 8008. А он в свою очередь — 8-разрядная версия 4004.

      Но делить даже некоторые младшие AVR не умеют как помню, хотя с ними я очень слабо знакома.


      1. sim-dev
        30.10.2015 08:36

        Делить AVR вообще все поголовно не умеют, а младшие (и старые) еще и умножать не умеют. А вот любой дедушка семейства MCS51 умеет и делить, и умножать.


        1. Musia17
          30.10.2015 10:22

          Вы правы! Спасибо! Ну я честно сказала что слаба знакома :)


        1. tronix286
          30.10.2015 12:35

          А вот у прадедушки 8048 MSC48 нет даже команды вычитания, что поначалу впечатляет.


          1. Musia17
            30.10.2015 12:53

            Ну это-то мелочи, т.к. вычитание зачастую это то же сложение. Я вот час назад разглядывая инструкцию к AVR (разбираясь с комментом выше) обнаружила что есть инструкция SUBI но нет ADDI — т.е. вычесть непосредственное значение можно а прибавить нельзя (что конечно не мешает вычесть отрицательное значение).


        1. EasyX
          30.10.2015 12:59
          -1

          mc51 — вообще отличный выбор для начинающего.


      1. beeruser
        30.10.2015 11:06
        +4

        Собсно Z80 это 8080 с доп.банком регистров


        Нет. Z80 просто обратно совместим с 8080. Помимо дополнительного банка регистров есть ещё ~500 дополнительных инструкций.

        «Делить» не умеют даже совсем нескромные Itanium-ы
        http://www.math.chalmers.se/Math/Grundutb/GU/MMG410/V11/FAQ/divsqrt.pdf


      1. googol
        31.10.2015 06:35

        У 8008 и у Z80 один создатель — Federico Faggin

        Мне понравилась вот эта его лекция по сабжу www.youtube.com/watch?v=hugZii_eX30


        1. beeruser
          31.10.2015 17:04

          История создания Zilog и Z80

          http://archive.computerhistory.org/resources/text/Oral_History/Zilog_Z80/102658073.05.01.pdf


  1. Mendel
    30.10.2015 10:31
    +2

    Умножают, не умножают…

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

    С эмуляторами тоже забавно недавно было. Знакомый сказал мол 580-й (клон 8080) он видел только в эмуляторе. Я сказал мол живьем видел, но лишь мельком, а на следующий день я нахожу у себя инструкцию по бейсику СМ1800, которая была моей первой машиной (у деда в лаборатории стояла, я собственно на этом бейсике и писал первые строчки кода). Открываю вики и вижу, что это тот самый 580...))


    1. icCE
      30.10.2015 11:23
      +5

      У меня не то, что клон. У меня оригинал работает стоит :) На голове появляется седина.

      AMD 8088-1


      1. hack2root
        30.10.2015 11:42

        У меня Intel 386 работал — я его полюбил за то что это реально был прорыв в то, без чего сейчас жизнь невозможна — многозадачные многопользовательские ОС.


        1. icCE
          01.11.2015 03:10

          Вы не поверите!!!

          80386


      1. jar_ohty
        31.10.2015 03:54
        +1

        Так это 8088, а не 8080, чей клон КР580ВМ80А. С этим имел дело любой пользователь икстишки.


        1. Musia17
          31.10.2015 07:45

          Если не ошибаюсь — просто 8086 с узенькой шиной данных?

          С программной точки зрения это ж почти никак не проявляется?


          1. icCE
            01.11.2015 02:59

            8086 — 16 битный процессор.
            8088 — 8 битный.

            Вообще надо будет как нить накатать статью про 80xx и вообще историю.


            1. icCE
              01.11.2015 03:15

              Перечитал и ужаснулся. Имел в виду разрядность шины данных.


      1. monah_tuk
        02.11.2015 03:20

        Пыль… Пожалейте его!

        А по сути: оно ещё и работает?


        1. icCE
          02.11.2015 09:39

          Да он вполне себе работает.
          Пыли на самом деле тут не много, это он уже успел сново запылится.

          вот тут у меня еще часть железок, если интересно.

          fotki.yandex.ru/users/icce/album/155960

          Правда не все.


        1. icCE
          02.11.2015 11:44

          Вот кстати нашел фото, делал пинги из DOS.

          twitter.com/icce/status/616294671585775616


  1. 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        
    


    1. Musia17
      30.10.2015 13:35
      +1

      а то получается ужас


      Да, ужас и есть — на сайте недавно задачу на минификацию кода э-э-э… вот она преобразовать BCD в HEX выложили — я пока и пробовать не стала. Хотя судя по тому что кто-то в 37 байт запилил — какой-то нюанс в условии, который я не вижу — а если втупую делать у меня кода на три страницы (и то не работает).


      1. 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
        


        1. Mrrl
          30.10.2015 15:24

          32 байта :)
          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
          


          1. Musia17
            30.10.2015 15:33

            М-м-м, по хорошему их не имеет смысла сюда постить — можно ж прям там засабмитить попытаться. А то секрет украдут :D


            1. Mrrl
              30.10.2015 15:44

              Действительно. Но убирать уже поздно.
              А там есть что-нибудь интересное?


              1. Musia17
                30.10.2015 15:51

                Там? Пряников и денег вроде не раздают :)

                Ну просто в статистике по этой задаче можно зависнуть в топе списка назло потомкам. Но в общем на любителя конечно… Я и не пыталась эту сдавать. Но подсматривать в Ваше решение не хочу пока. М.б. прозрение озарит! Тем не менее спасибо — вернусь к нему если отчаюсь!


  1. Loiqig
    30.10.2015 14:57
    +1

    Забавная вещь, в своё время по 8080 в институтские годы нас сильно гоняли. Были стенды с самим процессором и периферией и ручным набор машинных кодов на цифровой клавиатуре. Сами написали под него компилятор, чтобы всё время по табличке не переводить, но вводить в любом случае вручную приходилось.

    Спасибо, за статью и нахлынувшие тёплые воспоминания.


  1. andy_p
    03.11.2015 00:07

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


    1. icCE
      03.11.2015 11:26

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


      1. Musia17
        03.11.2015 14:32

        Да умножение-то дело нехитрое, я смутно припоминаю что умножение последовательным удвоением еще древние вавилоняне умели (т.к. они считали в 60-ричной системе и таблицы умножения запоминать было нереально).

        Деление обычно самое нудное…