Как стать автором
Обновить

Комментарии 29

вам еще учиться и учиться..

Да, это так, но я уверен, что с дальнейшей практикой и обучением я смогу достичь ещё более высокого уровня :)

Прежде всего нужно научиться читать даташиты (например, прочитать, что есть не только аккумулятор и указатель команд, но и регистр переносов, регистр стека, регистры общего назначения). Освоить двоичную арифметику (что б не было "вы не можете получить число выше 15"). А уж потом браться за эмуляторы процессоров. Ничего сложного нет, но вы слишком рано начали делать эмулятор.

Минусовать не стал чисто из-за того, чтоб не отбить желание программировать. Но плюсов вы точно не заслужили.

Спасибо, что уделили внимание моей статье и что даёте различные советы на будущее)

Да пожалуйста. Если нужна будет конкретная помощь - обращайся в личку, свяжемся, например, в телеге.

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

Да, статья короткая, но зато без воды. Как раз для быстрого чтения в перерывах между работой например)

Ну, elif для всех опкодов использовать эт конечно мощно :)) Я когда-то в 14 лет написал интерпретатор BASIC-подобного языка и похожим образом реализовывал команды, причём таким образом были реализованы и основные конструкции (объявление функций, if/else, циклы), но так делать не стоит.

От абстрактного эмулятора процессора толку не очень много. Но гораздо интереснее эмулировать уже готовые устройства: с экранчиком, кнопочками или звуком, можете даже что-то своё придумать. Потом этот "эмулятор" можно воплотить в настоящее аппаратное устройство - например, на ESP32 ;)

По советам: выделите отдельный словарь (коллекцию вида ключ = значение) для каждого опкода и реализацию для него, а-ля так (Python не знаю, но код должен быть понятен):

void opMovImmediate(BinaryReader reader)
  {
    byte reg1 = reader.ReadByte(), reg2 = reader.ReadByte();

    ...
  }

Dictionary<byte, Action<BinaryReader>> opcodes;

...

opcodes.Add(I4004.MovImmediate, opMovImmediate);

byte opcode = reader.ReadByte();

if(opcodes.ContainsKey(opcode))
  opcodes[opcode](reader);
else
  throw new ArgumentException("Unimplemented opcode " + opcode);

Сами номера опкодов лучше выделить в отдельное перечисление. Можно вот так:

enum I4004
{
  Nop = 0,
  MovImmediate = 0x2
}

Регистры можно реализовывать по разному, однако учтите, что в x86 и некоторых других архитектурах с подходом а-ля один регистр - одна переменная не прокатит, поскольку bl/bh - младшие разряды "большого" 16-битного регистра bx и.т.п. Можно представить регистры как кучу и адресовать его соответственным образом.

Плюсик всё равно поставил. Пилить эмуляторы и интерпретаторы - дико увлекательное и интересное занятие на самом деле.

Благодарю за Ваши советы. Постараюсь их учесть :)

одна переменная не прокатит, поскольку bl/bh - младшие разряды "большого" 16-битного регистра bx

Почему же?

struct registry_x
{
    byte l;
    byte h;

    operator word () { return h << 8 + l; };
    operator = (word w) { l = word & 0xff; h = word >> 8; };
};

registry_x bx; // Одна переменная )))

bx.l; = 1;
bx.h; = 2;
bx = 3;

Такое можно делать только в некоторых языках, да и придётся помнить про Big/Little Endian

Я бы вообще предпочёл union или type… забыл термин. Когда (byte*) &my_word. Но с современными стандартами и компиляторами, куда ни плюнь — всюду UB.

В Паскале это кажется называлось absolute, или case-type, типа такого:

type registersplit=record
  case x of { неважно что писать в case..of, вплоть до бреда }
    1: l:byte; h:byte;
    2: x: word;
    3: ex: longint; { а не было dword в турбо-6.0 }
  end;
end;
var a:registersplit;
begin
   a.h=32;
   writeln(a.x); { выведет 32*256=8192 ибо DS инициализируется нулями, a.l=0 }
end.

Не знаю, есть ли это вот ещё в Delphi/Lazarus, но если есть, точно не должно вести себя как UB.

опкоды лучше (особенно в учебных целях) разбирать побитно - становится понятно, как процессор дешифрует команды.

Я не знаю, какая ISA взята за основу, но это точно не i4004. Регистра B в i4004 не существует (в i8008 - есть), а регистры просто индексные - rr0..rr15. Опкоды левые. Некоторых инструкций даже в теории нет в i4004 (HLT появился только в i4040).

Запись в память куда сложнее, чем просто одна инструкция. Да и память не линейна, о организована в виде банков и регистров памяти.

Если что, у писал 2 эмулятора (i4004 и i4040), которые совместимы с железом с точностью до тактов.

Скажите пожалуйста, верные ли опкоды для инструкций я нашёл?
NOP (No Operation): опкод - 0000.
ADD (Сложение): опкод - 0001.
SUB (Вычитание): опкод - 0100.
AND (Логическое И): опкод - 0111.
OR (Логическое ИЛИ): опкод - 1000.
Загрузка числа в аккумулятор (acc): опкод - 1001.
Загрузка числа в регистр: опкод - 1101.

Нет, неверные.

Советую почитать релевантные доки - MCS-4_Assembly_Language_Programming_Manual_Dec73 / MCS-4_UsersManual_Feb73 / MCS4_Data_Sheet_Nov71. Или даже просто datasheet на процессор.

Спасибо за информацию. В datasheet я мельком заглядывал сегодня, ещё раз загляну :)

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

Ну что Вы, в наше время без классов даже hello world нельзя писать. И ещё желательно минимум пять уровней абстрактных классов ))

Можно даже попробовать сделать для этого эмулятора свой язык программррвания

Знаете, это было бы очень здорово))

я б посоветовал сделать нормальный эмулятор, а для него - написать свой ассемблер. Бесполезно в плане применения, но даст хорошую практику.

Что-то потипу смеси питона и плюсов. Ну либо 0х00 можно только через return 0 сделать нормально, а если автоматически как в питоне, я это даже не представляю как реализовать

Спасибо тебе дружище за эту статью. Очень давно интересовала тема ассемблера и как оно все работает на самом низком уровне, но никак не мог подступиться. Увяз в чтении литературы, подобной Таненбауму и т. д. Вместо того, чтобы просто взять и начать что-то делать, мне всегда кажется что еще рано, мало знаний, надо еще что-то почитать. Твоя статья и твой до неприличия простой и понятный код, с простым примером вдохновили меня. Оказывается вот оно как просто. Машина просто шаг за шагом берет и читает инструкцию, выполняет ее, а потом переходит к следующей. Благодаря тебе, я скачал эмулятор 8086 и начал изучать ассемблер, жутко интересно. Очень хочется уловить момент перехода с ассемблера на более-менее сносный компилятор какого-нибудь простого ЯП.

Я очень рад, что Вам понравилось. Я старался :)

Очень хочется уловить момент перехода с ассемблера на более-менее сносный компилятор какого-нибудь простого ЯП

Найдите книгу Хендрикс "Компилятор Small-c для микро-ЭВМ" (изд. Радио и связь, 1989) - там это объясняется практически на примере. Только кодогенерация для ассемблера 8080.

Эмулятор, написанный на интерпретируемом языке - это сильно. Не удивлюсь, если физический 4004 из семидесятых будет быстрее эмулятора ))

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

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории