Сказ о том, как я эмулятор Intel 4004 на Python писал (часть 2)

от автора

Небольшой дисклеймер: перед прочтением данной статьи ознакомьтесь с первой частью, дабы вникнуть в суть происходящего. Желаю вам приятного прочтения 🙂

Вступление

Сидел я тут на днях и думал, как можно улучшить мой эмулятор «Intel 4004» и перечитывая комментарии под первой частью, я осознал одну очень простую вещь — моё творение на 4004-ый не очень то и похоже.. Абсолютно рандомные опкоды, инструкции, которых в данном процессоре отродясь не было, например, инструкции HLT, AND и OR (HLT так вообще появилась только в Intel 4040).

После некоторых раздумий я принял следующее решение — нужно переписать эмулятор с нуля, с корректными опкодами, инструкциями и так далее 😉

Как всё писалось?

Я начал активно смотреть datasheet, стал рассматривать другие проекты по теме 4004-го, особенно мне понравился эмулятор пользователя markablov, написанный на языке JavaScript (именно оттуда впоследствии были взяты необходимые опкоды).

Как и в прошлый раз, я создал класс CPU и начал с реализации памяти (насущные 256 байт), аккумулятора и счётчика команд (память в этот раз я запихал в инициализацию для удобства):

class CPU:     def __init__(self):          # 256 bytes of memory         self.memory = bytearray(256)          # accumulator         self.acc = 0          # program counter         self.pc = 0

В этот раз в эмуляторе используется всего 7 инструкций из 46 возможных (так что полноценным его назвать нельзя, скорее урезанным, в прошлый раз было также).

Список инструкций из datasheet

Список инструкций из datasheet

Вот список используемых инструкций:

Инструкция

Описание инструкции

NOP

Без операции

INC

Увеличение индексного регистра

ISZ

Пропуск индексного регистра, если он равен нулю

ADD

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

SUB

Вычитание индексного регистра из аккумулятора с заимствованием

LD

Загрузка индексного регистра в аккумулятор

XCH

Обмен индексного регистра и аккумулятора

Их реализация была выполнена путём создания функций:

    # NOP instruction (No Operation)     def NOP(self):         self.pc += 1      # INC instruction (Increment index register)     def INC(self):         self.acc = (self.acc + 1) % 256         self.pc += 1      # ISZ instruction (Increment index register skip if zero)     def ISZ(self, address):         self.memory[address] = (self.memory[address] + 1) % 256          if self.memory[address] == 0:             self.pc += 2         else:             self.pc += 1      # ADD instruction (Add index register to accumulator with carry)     def ADD(self, address):         self.acc = (self.acc + self.memory[address]) % 256         self.pc += 2      # SUB instruction (Subtract index register to accumulator with borrow)     def SUB(self, address):         self.acc = (self.acc - self.memory[address]) % 256         self.pc += 2      # LD instruction (Load index register to Accumulator)     def LD(self, address):         self.acc = self.memory[address]         self.pc += 2      # XCH instruction (Exchange index register and accumulator)     def XCH(self, address):         temp = self.acc         self.acc = self.memory[address]         self.memory[address] = temp         self.pc += 2

Обо всём по порядку:

  • NOP просто увеличивает значение счётчика команд (pc) на 1, что позволяет перейти к следующей инструкции в программе.

  • INC увеличивает значение аккумулятора (acc) на 1, ограничивая его значением до 0-255, и затем увеличивает pc на 1.

  • ISZ увеличивает значение в ячейке памяти с заданным адресом на 1, снова ограничивая его до 0-255. Если значение в ячейке становится равным 0, pc увеличивается на 2, иначе увеличивается на 1.

  • ADD добавляет значение из ячейки памяти с заданным адресом к значению аккумулятора, ограничивает результат до 0-255 и увеличивает pc на 2.

  • SUB вычитает значение из ячейки памяти с заданным адресом из значения аккумулятора, ограничивает результат до 0-255 и увеличивает pc на 2.

  • LD загружает значение из ячейки памяти с заданным адресом в аккумулятор и увеличивает pc на 2.

  • XCH обменивает значение аккумулятора и значение в ячейке памяти с заданным адресом, увеличивает pc на 2.

Дальше была создана функция run, в которой были прописаны опкоды с выполнением определённой функции:

    def run(self):         while self.pc < len(self.memory):             opcode = self.memory[self.pc]              # NOP instruction opcode             if opcode == 0x0:                 self.NOP()              # INC instruction opcode             elif opcode == 0x6:                 self.INC()              # ISZ instruction opcode             elif opcode == 0x7:                 self.ISZ(self.memory[self.pc + 1])              # ADD instruction opcode             elif opcode == 0x8:                 self.ADD(self.memory[self.pc + 1])              # SUB instruction opcode             elif opcode == 0x9:                 self.SUB(self.memory[self.pc + 1])              # LD instruction opcode             elif opcode == 0xA:                 self.LD(self.memory[self.pc + 1])              # XCH instruction opcode             elif opcode == 0xB:                 self.XCH(self.memory[self.pc + 1])              else:                 print('Unknown opcode!!!')                 return              self.pc += 1

Как составляется программа?

Программу надо составлять прямо в коде, а конкретно в program.py. Вот пример программы, которая отнимает от числа 12 число 5 и прибавляет число 2:

from cpu import CPU  cpu = CPU()  # Write the numbers 12, 5 and 2 to memory at arbitrary addresses (e.g. 0x10, 0x11 and 0x12) cpu.memory[0x10] = 12 cpu.memory[0x11] = 5 cpu.memory[0x12] = 2  # Execute the commands to subtract the numbers 12 and 5, and then add the number 2 to the resulting number cpu.LD(0x10) cpu.SUB(0x11) cpu.ADD(0x12) cpu.NOP()  # The result of the program will be stored in the accumulator print('') print(f'  Result: {cpu.acc}') print('')
Результат программы

Результат программы

Заключение

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

У меня есть следующие идеи по развитию проекта на будущее:

  1. Добавить ещё больше инструкций 4004-го.

  2. Используя библиотеку tkinter, создать окно, где пользователь вводит программу и ему выводится результат (дабы не приходилось устанавливать сам Python, различные IDE к нему для запуска и теста эмулятора).

Полный код можно посмотреть на моём GitHub.

С вами был Yura_FX. Спасибо, что дочитали данную статью до конца. Не забывайте делиться своим мнением в комментариях 🙂


ссылка на оригинал статьи https://habr.com/ru/articles/805825/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *