Перейти к содержанию
    

Alikberov

Участник
  • Постов

    54
  • Зарегистрирован

  • Посещение

Репутация

0 Обычный

Информация о Alikberov

  • Звание
    Участник
    Участник
  • День рождения 17.08.1979

Контакты

  • Сайт
    Array

Информация

  • Город
    Array

Посетители профиля

Блок последних пользователей отключён и не показывается другим пользователям.

  1. Это всё понятно, так как тоже в Logisim в 2019 году и сделал рабочий прототип своего процессора. Сначала Гарвардский, что совсем легко. Потом неделями отлаживал Принстонский, так как пришлось более чётко расписать всё по тактам. Но, так уж сложилось, что о RISC'ах я ещё узнал из статьи «RISC - Путь в будущее» журнала РАДИО (ссылка). Однако, как воспитанный на CISC'ах, консервативно занимаюсь именно этим. К тому же, сам дизайн этого устройства я начинал как шуточную пародию на RISC, типа: А пускай код A1 и выбирает регистр A1 на схеме А пускай код CF инвертирует флаг CF А пускай код BC означает операнды A,B для АЛУ-операций А главное, чтобы код 00 был командой Останова HLT (как Zero-Terminated String) То есть, каждый код делал только одно незаконченное действие и для выполнения одной эффективной операции требовалось как минимум три команды. Но, по мере доработок и наработок, развития JavaScript-эмулятора, оказалось, что теперь можно получить вполне серьёзную модель полноценного процессора… И который год никак не могу его охватить концептуально весь, что не позволяет даже на высоком уровне JavaScript описать эмуляцию на 100%. Его легко эмулировать на i8080 (ссылка запуска) или z80, что позволит исполнять код даже на ZX-Spectrum. Кто работает под Linux, может попробовать эмулятор на Bash'е (пока всё ещё не дописал, но отладчик с ассемблером имеется), который писался под Raspberry Pi для управления периферией. BashEmu.zip
  2. Вроде бы и отладки не требует такой модуль: И так всё очевидно, понятно… module bcd_accumulator (input wire clk // Clock (positive edge) ,input wire bcd_en // Enable BCD accumulation ,input wire bcd_clr // Clear BCD accumulator ,input wire [7:0] operation // Operation code (BCD only) ,output reg [15:0] bcd_output // Full binary summ from BCD ,output reg bcd_exist // Flag of BCD ); wire [3:0] nibble; assign nibble = &operation[3:0] ? operation[7:4] : operation[3:0]; always @(posedge clk) begin if(bcd_clr) begin bcd_output <= 16'd0; bcd_exist <= 1'd0; end else if(bcd_en) begin bcd_output <= (((bcd_output << 2) + bcd_output) << 1) + {12'd0, nibble}; bcd_exist <= |nibble | bcd_exist; end end endmodule Здесь можно отметить одно важное нововведение: Триггер «bcd_exist»… Сам аккумулятор вектора его не имеет и при указании смещения 65536 оно, естественно, переполняется и становится нулевым. Это - опасная недокументированная ситуация, которая может выработать маргинал! Например: «10» - указатель «[D1+0]» «10 10» - маргинал #1 «10 10 10» - маргинальный указатель «1#[D1+0]» «16» - указатель «[D1+6]» «16 15» - указатель «[D1+65]» «16 15 15» - указатель «[D1+655]» «16 15 15 13» - указатель «[D1+6553]» «16 15 15 13 16» - указатель «[D1+0]», так как 65536 уже 17-битная величина 0x10000 «16 15 15 13 16 10» - побочный маргинал #1: Это уже из разряда эзотерического кодирования «из ряда вон!» - «маргиналище: маргинальный маргинал!» «16 15 15 13 16 10 11» - маргинальный указатель «1#[D1+1]» (концептуальная недопустимость!) Теперь флаг «bcd_exist» будет сигнализировать о наличии в смещении самой ненулевой тетрады, что никак не зависит от переполнения: маргинал посреди указателя не сможет образоваться. Важная модификация: Здесь тетрада «nibble» может использовать как младшие биты кода операции (работает на префиксах и командах REG), так и старшие биты (специально для инструкции FOR/MOV). «AA A1 3F» - команда «MOV A1,A3» «AA A1 A2 3F» - команда «MOV A12,A3» «AA A1 3F 4F» - команда «MOV A1,A34» «AA A1 A2 3F 4F» - команда «MOV A12,A34» «AA A1 A2 A3 A4 A5 6F 7F 8F 9F 0F» - команда «MOV A12345,A67890» В таких ситуациях, так как MOV - не операция АЛУ и может без вреда исполняться много раз, то принято решение ввести такой механизм. Для чего этот модуль должен сам уметь ориентироваться…
  3. Сегодня, на свежую голову, пришла одна любопытная мысль… Процессоры i8085 и i8086 имеют мультиплексированную шину адреса и данных, предназначенную для работы с соответствующими ИМС. Тут подумалось, что везде совмещали именно D0-D7 и A0-A7… А что, если совместить D0-D7 с A8-A15, так как старшая часть адреса меняется относительно реже младшей? То есть, сделать ход конём в рамках конкретной реализации, чтобы сэкономить на тактах: Использовать мелкий внешний регистровый файл, который мог бы переключаясь сам выдавать старшие биты адреса… Вот на этом моменте выясняется, что вся проблема реализации в том, что она затачивается под конкретную шинную архитектуру. Мне нужно разработать сухое ядро, которое уже будет обвешиваться всяческими контроллерами шин. ЯДРО Должно обрабатывать поток машинного кода в формальном виде, как маршрутизатор Должно подключаться к регистровому файлу любого исполнения: Монолитное внутреннее или внешним кэшем Должно только вырабатывать управляющие сигналы для работы всех узлов, входящих в состав процессора Тем самым, параметр разрядности не должен иметь никакого значения (чем-то напоминает процессорную секцию), как и архитектурное исполнение (Гарвардское или Принстонское). На данный момент у меня более-менее разработан и отлажен лишь контроллер шины: Здесь: bus_measure - сигнал о разрешении выполнения текущего машинного цикла bus_lock - сигнал о разрешении переключения шины адреса Эти сигналы введены относительно недавно и отвечают за задержки всего остального. Иначе говоря, этот модуль получился основным задающим и остановить его нельзя: Только притормозить. Думаю, именно «bus_measure» должен, в принципе, управлять Ядром в плане скорости, как я понимаю… То есть, по «bus_lock» отдельные исполняющие (Регистровый Файл, Вычислитель Эффективного Адреса и Управление УВВ) архитектуры должны выполнять свой цикл… Вот здесь, как ведущий инженер-конструктор проекта, я чувствую себя никак! У меня нет базовых представлений о маршрутизации. Дешифратор команд также более-менее закончил: Вот на этих трёх узлах (включая вычислитель эффективного адреса, описанный выше) сейчас всё держится и работает (прежде всего, отладчик-дизассемблер на System Verilog).
  4. Ещё учитывая то, что я сейчас третий год на Raspberry Pi 4 / 8 Gb сижу, тогда как основной ПК включался года полтора назад с единственной целью - успеть скопировать файл с паролями от нужных аккаунтов на флешку. После чего, не прошло и минуты - BSOD'нуло… То есть, все VMware с Windows'9x, Windows'XP, Windows'8, а также с Visual Studio 6, Proteus, Quartus и т.д. - на диске ПК, который BSOD'ами кидается в самый неподходящий момент… Менял БП и Винт: дело, ИМХО, в материнке… Тем самым, все практические наработки эмуляторов и т.д. - всё на локальном диске: В сеть внутренние файлы не кидал… Приходится в рамках Raspberry Pi всё восстанавливать по памяти и по опыту… Из последних разработок (из-под Raspberry Pi) сейчас имеется уже симулятор MMX/SSE отладки (статья на Хабре) и текстовый редактор для РАДИО-86РК с поддержкой светового пера (ссылка), чтобы не терять спортивной 8-битной формы. Вот думаю его приспособить движок под обновлённый эмулятор Койяанискаци. В этом свете, производительность, действительно, не имеет никакого значения.😂 Четыре года назад я данный бутафорский процессор за пару часов схематикой в Logisim намалевал. Однако, уж четвёртый год никак его до ума довести не могу. Сами видите, он сложнее Z80 уже получается, а с его этими маргиналами, сопроцессорами и безразмерным регистровым файлом - дотягивает до i8086 и не знаю, как вообще разобраться! Это как-то бездонная архитектура получилась. Хоть бери и описывай три варианта: Mini / Classic / Pro. Естественно, я сейчас барахтаюсь где-то в промежутке между Classic и Pro…😇 Я специально указал на проблемы архитектур Intel в частности, так как у них: Флаговый регистр имеет какие-то резервные биты #1, #3 и #5. Они резервные уж начиная с i8080 и как рудимент - так ими и остаются Инструкция LEA вторым операндов всегда принимает указатель. При попытке использовать обычный регистр получаем исключение Инструкция LOOP была некогда классной, а теперь всюду рекомендуют избегать её использования Инструкции ENTER/LEAVE так браво ввели для Паскаля, а на деле - замусорили таблицу команд, как и AAA, AAD, AAM, AAS У меня единственным перегруженным сейчас является набор АЛУ-команд, как можно видеть чуть выше из логов, где появились всякие знакомые LEA, XLAT и т.д. А всего-то, я строго запретил использовать флаговый регистр A0 за операнд и указывать D-порт, где не следует… Сейчас у меня концептуально ещё веселее: кодом AB указываем, что левый операнд-приёмник - группа регистров A, правый операнд-транслятор - группа регистров B кодом A1 указываем, что конкретно операнд-приёмник - регистр A1 кодом A2 указываем, что мы - передумали и операнд-приёмник A2. Так дурачиться нельзя: Раз был A1 и теперь A2 - получайте A12! кодом C3 указываем, что выбираем операнд-приёмник - регистр C3. Но, как же так, если о группе регистров C пока и речи не было? Получайте синтетическую инструкцию «MOV A12,C3» кодом C4 указываем, что мы всё ещё не готовы остановиться и упорно выбираем снова сторонний регистр! Вот тут то получаем синтетическую инструкцию «MOV A12,C34» Вы поймали волну?😉 Вроде бы всё просто, но у процессора как будто появилось чутьё контекста кода: Он запоминает предыдущее действие и позволяет последовательно его детализировать. А какой программист откажется от такого вот безразмерного регистрового файла? То есть, парадигма красивого машинного кода в HEX-дампе позволяет кодировать такие вот простые и изящные инструкции!😆 А как Вам такое? код «A1» всегда указывает на регистр A1 как инструкция «REG A1»… код «A1 A0» по новой интерпретации однотипных кодов как инструкция «REG A10» указывает на регистр A10 код «A0 A1» при таком принципе будет интерпретирован как инструкция «REG A01», что по сути «REG A1» при игнорировании лидирующих нулей (NOP'ы?) код «A0 A0 A1» как инструкция «REG A001» по сути - снова «REG A1» (с NOP'ами???) Вот как тут быть? Как вариант, есть такая черновая интерпретация: код «A0 A1» условно считать за укороченный «REG A101»: Здесь лидирующий ноль включает режим единиц первого десятка сотни код «A0 A0 A1» условно считать за укороченный «REG A1001» код «A0 A9 A9» условно считать за укороченный «REG A1099» Но, такое усложнение только больше всё запутает и маргинализировать лидирующие нули я просто опасаюсь чисто из концептуальных соображений: Маргиналы и так усложнили базовые понятия об архитектуре и кодировании алгоритмов, но не завели её в «бездну эзотерических битовых полей», как это произошло с префиксами Z80 и в x86. Например, все новые инструкции (INC/DEC/CLR/SET/NOT/LEA/ORD/LEX) порождаются из простейших понятий о том, что: регистр A0 (PSW) не может участвовать в операциях АЛУ как активный операнд-приёмник и/или операнд-источник регистры D-группы как 16-битные регистровые пары не могут смешиваться с 8-битными одиночными регистрами в операциях АЛУ регистры D-группы могут представлять порты УВВ как источники в 8-битных операциях АЛУ один и тот же регистр не может в большинстве операциях АЛУ быть первым и вторым операндом одновременно Уже эти правила порождают десятки исключительных ситуаций с новыми полезными инструкциями (даже с игнорированием маргиналов😞 +--------------> Имеется "маргинальный префикс" | | +------------> Имеется "векторный префикс" | | | | ++---------> Приёмник - либо A0/PSW, либо порт Dj, либо 16-битная регистровая пара Bn:Cn | | || | | || +-------> Приёмник и источник - один РОН или регистровая пара | | || | | | || | ++----> Приёмник - либо A0/PSW, либо порт Dj, либо 16-битная регистровая пара Bi:Ci | | || | || | | || | || +--> Признак FOR/MOV - не АЛУ-операции | | || | || | M V AD E AD F X_1_10_1_XX_1: MOV PSW,[V] ; Чтение PSW из ОЗУ X_1_10_1_XX_0: UNARY [V] ; Унарные INC/DEC/CLR/SET/NOT над ОЗУ X_0_10_1_XX_1: MOV??? PSW,PSW ; 1 шт. !!!reserved!!! X_0_10_1_XX_0: UNARY?? PSW ; 5 шт. !!!reserved!!! X_1_10_X_01_1: SWAP [V],Dn ; XCHG ОЗУ и 16-битной пары X_1_10_X_01_0: ALU_OP [V],Dn ; АЛУ-ADD/SUB/AND/OR/EOR 16-битного ОЗУ и пары X_0_10_X_01_1: INF Dj ; MOV PSW,Dj - Попытка ввода из порта (результат - в CF) X_0_10_X_01_0: UNARY Dn ; 16-битные унарные INC/DEC/CLR/SET/NOT X_1_10_X_XX_1: SWAP [V],Rn ; XCHG ОЗУ и 8-битного РОН X_1_10_X_XX_0: ALU_OP [V],Rn ; АЛУ-ADD/SUB/AND/OR/EOR ОЗУ и 8-битного РОН X_0_10_X_XX_1: MOV PSW,Rn ; Чтение PSW из РОН X_0_10_X_XX_0: UNARY Rn ; Унарные INC/DEC/CLR/SET/NOT над РОН X_1_01_X_10_X: ALU/MOV Dn,[V] ; АЛУ-ADD/SUB/AND/OR/EOR и MOV пары с 16-битным ОЗУ X_0_01_X_10_1: OUF Dj ; MOV Dj,PSW - Попытка вывода в порт (результат - в CF) X_0_01_X_10_0: UNARY Dn,CF ; Унарные INC/DEC/CLR/SET/NOT над парой при условии CF X_1_00_X_10_X: ALU/MOV Rn,[V] ; АЛУ-ADD/SUB/AND/OR/EOR и MOV РОН с ОЗУ X_0_00_X_10_1: MOV Rn,PSW ; Загрузка PSW в РОН X_0_00_X_10_0: UNARY Rn,CF ; Унарные INC/DEC/CLR/SET/NOT над РОН при условии CF X_1_01_1_XX_1: LEA Dn,[V] ; Загрузка "эффективного адреса" в регистровую пару X_1_01_0_01_1: ORD Dn,[V],Di ; Dn = (MAX(Dn, Di) - ОЗУ) >> 1 X_1_01_X_01_0: ALU_OP Dn,[V],Di ; 16-битная ADD/SUB/AND/OR/EOR ОЗУ с Di, результат в Dn X_0_XX_1_XX_1: MOV??? R,R ; 1 шт. Холостая пересылка (NOP???) X_0_01_1_XX_0: UNARY?? Dn ; 5 шт. 16-битные унарные INC/DEC/CLR/SET/NOT (повтор!!!) X_1_01_0_XX_1: LEX Dn,[V],Ri ; 16-битный XLAT: MOV Dn,[V+2*Ri] X_1_01_X_XX_0: ALU_OP Dn,[V],Ri ; 8-битная ADD/SUB/AND/OR/EOR ОЗУ с РОН, результат в 16-бит Dn X_0_01_0_00_1: MOV Dj,Ri ; Аналог OUT Dj,Ri - вывод в порт X_0_01_X_XX_0: ALU_OP Dn,Ri ; 16-битная ADD/SUB/AND/OR/EOR Dn с 8-битным Dn X_1_0X_X_01_1: MOV??? R,[V],Dj ; 1 шт. !!!СТРАННАЯ ОПЕРАЦИЯ!!! X_1_0X_X_01_0: ALU_OP R,[V],Dj ; 8-битная ADD/SUB/AND/OR/EOR ОЗУ с портом Dj, результат в РОН X_0_0X_X_01_X: ALU/MOV R,Dj ; АЛУ-ADD/SUB/AND/OR/EOR или MOV (IN R,Dj) РОН с портом Dj X_1_0X_1_0X_1: LEX R,[V] ; 8-битный XLAT: MOV R,[V+R] X_1_0X_0_0X_1: ORD R,[V],R' ; R = (MAX(R, R') - ОЗУ) >> 1 X_1_0X_X_01_X: ALU/MOV R,[V],R' ; ADD/SUB/AND/OR/EOR или MOV ОЗУ с R', результат в РОН X_0_0X_X_01_X: ALU/MOV R,R' ; ADD/SUB/AND/OR/EOR или MOV ОЗУ с R', результат в РОН Примеры:______________________________________________________________________________________________ AA A0 12 0F MOV PSW,[D1+2] ; На самом деле, полная мнемоника операции - MOV A0,[D1+2],A0 AA A0 12 0E NOT [D1+2] ; На самом деле, полная мнемоника операции - EOR A0,[D1+2],A0 AD A0 12 3F SWAP [D1+2],D3 ; На самом деле, полная мнемоника операции - MOV A0,[D1+2],D3 AD A0 1F INF D1 ; На самом деле, полная мнемоника операции - MOV A0,D1 AD A0 1E NOT D1 ; На самом деле, полная мнемоника операции - EOR A0,D1 AB A0 12 3F SWAP [D1+2],B3 ; На самом деле, полная мнемоника операции - MOV A0,[D1+2],B3 AB A0 12 3E EOR [D1+2],B3 ; На самом деле, полная мнемоника операции - EOR A0,[D1+2],B3 AB A0 1F MOV PSW,B1 ; На самом деле, полная мнемоника операции - MOV A0,B1 AB A0 1E NOT B1 ; На самом деле, полная мнемоника операции - EOR A0,B1 DA D1 0F OUF D1 ; На самом деле, полная мнемоника операции - MOV D1,A0 DA D1 0E NOT D1,CF ; На самом деле, полная мнемоника операции - EOR D1,A0 DD D1 23 1F LEA D1,[D2+3] ; На самом деле, полная мнемоника операции - MOV D1,[D2+3],D1 DD D1 23 4F ORD D1,[D2+3],D4 ; На самом деле, полная мнемоника операции - MOV D1,[D2+3],D4 DC D1 23 4F LEX D1,[D2+3],C4 ; На самом деле, полная мнемоника операции - MOV D1,[D2+3],C4 P.S.: Но это всё - лирика…
  5. Никак нет, Товарищи! Не забил… Просто ещё два года назад с этими маргинальными префиксами разобраться не мог на концептуальном уровне (какой там эмулятор/ассемблер дорабатывать, если обнаружилась такая, буквально феноменальная, ситуация) - нужно было уйму бумажной работы проделать, чтобы самому понять, на что годится такое. Ну, раз у Intel'а есть загубленный набор Escape-инструкций под FPU, то здесь архитектурно нет никаких помех… Так как Escape-набор инструкций i8086 был задуман под любой сопроцессор, ранок с его бухгалтериями требовал именно i8087. Хотя можно было в систему проецировать, помимо FPU, ещё и GPU и т.д. Но, i486 со встроенным FPU окончательно набор всех Escape-инструкций закрепил за FPU. И в сетях имеются очерки сожалений инженеров о том, «как быстро FPU стал народным»… Практически, купив шестнадцатеричные тумблеры в количестве тысяч, можно напаять аналог ПЗУ с механической "подстройкой кода"… Ну, о сравнении ничего сказать не могу. А вот о всяких вкусняшках - немного поведаю. Долгое время последовательность однотипных кодов выполнялась как NOP'ы. Так, коды «A1 A2 A3» последовательно активировали регистры A1, A2, A3 и т.д. Эффективным оказывался именно последний - A3. Теперь такая ситуация исключена - появился механизм выявления повторного выбора в пределах регистровой группы… Сейчас последовательность «A1 A2 A3» интерпретируется как «REG A123»! То есть, был выбор одного из десяти, теперь - практически ничем не ограничен… Практически, как «процессор военного класса», появилась «масштабируемость регистрового файла». Правда - только в концепции. Ни в эмуляторе, ни в Verilog поддержки нет. Но, Verilog-модель я научил такие последовательности, как и маргиналы обрабатывать в формальном виде. Технически особых проблем нет, если концепция понятна. Вот пока не понятно, как обеспечить такой большой регистровый файл… Если указать коды «D1 D2 D3 D4 D5» - получим инструкцию «REG D12345» (16-битный регистр №12345): Никакого регистрового файла не хватит! (Если только, как в фильме «2001: Космическая Одиссея» - не вкручивать дополнительные модули регистровых файлов в HAL-9000…) То есть, говоря в двух словах: Размер Регистрового Файла не ограничен ничем. В случае с FPGA в Verilog-модели можно указывать параметр размерности Число внешних сопроцессоров не ограничивается ничем. Они просто должны перехватывать маргиналы и обрабатывать их сами (как тот же i8087) Сейчас проблем несколько: Нет понимания организации (и описания) модуля Регистрового Файла, который мог бы изменять размер как в синтезируемом виде FPGA, так и просто использовать внешний кэш Нет понимания организации обработки прерываний. Ситуации "отсутствия аппаратного сопроцессора под маргинал" или "регистровый файл слишком мал" должны как-то программно обрабатываться То есть, если Вам нужен простой 8-битный процессор с памятью до 64 Кб, но с 16777216-ю 8-битными аккумуляторами и масштабируемый как Мэйн-Фрейм, то «Добро пожаловать в мою архитектуру!»…
  6. Так уж случилось, что из-за поломки основного ПК всё оказалось на долгой паузе... Все, кто знаком и работает с архитектурой x86 / IA32, давно в курсе, что она - далека от совершенства… Но, так как на разработку конкретной архитектуры всегда влиял так или иначе Intel, буду приводить примеры на IA-32 для доходчивости… LEA EBX,[EBX+EBX*2+3] ; Знакомый всем трюк продвинутого ADD ADD EBX,[EBX+EBX*2+3] ; Здесь получим исключение, так как регистр базы и регистр индекса - один. Что "из ряда вон!" ("маргинал" какой-то…) Вот такие случаи в Intel никак не предусмотрели и оставили всё на совести программиста, переложив всю заботу на обработчик исключения. MOV EAX,FS:[0x00000018] ; Встречается порою вполне знакомое… MOV EAX,CS: DS: ES: FS:[0x00000018] ; Процессор проигнорирует в этой куче три лишних префикса. Но, запись - "из ряда вон!" ("маргинал" какой-то…) И такие случаи в Intel демократично проигнорировали и оставили на совести программиста… Теперь читатели темы достаточно подготовились к нововведению… МАРГИНАЛЫ Как уже говорилось многим выше, префиксы кодируются с помощью любого ненулевого BCD. код «10» означает указатель на ячейку в памяти по адресу «[D1+0]» (левый ниббл адресует регистр базы, правый ниббл определяет относительное смещение) код «10 23» усложняет указатель добавлением к базовому ещё и индексного регистра, что получается «[D1+D2+3]» код «10 23 45» добавляет ещё один индексный регистр и теперь получилось «[D1+D2+D4+35]» (относительное смещение ожидаемо множится на 10 и накапливает правые нибблы) код «10 23 45 16» получился недопустимым, как «[D1+D2+D4+D1+356]»! Так какой же регистр здесь можно считать базовым, если D1 повторяется вновь? Как уже можно догадаться, это - та самая ситуация "из ряда вон!" с условным кодовым именем "маргинал какой-то"… Естественно, для решения этой задачи не требуется никакой вычислительной мощности, так как здесь - концептуальная проблема. На решение этой проблемы у меня ушёл, без малого, год… Год, в часы досуга, на листке бумаги карандашом расписывались подобные ситуации с поиском решения. И решение было найдено достаточно изящное и гибкое! код «10 23 45 16» следует интерпретировать как «[D1+(D2+D4)*2+356]», где повторяемый регистр базы служит признаком удвоения суммы индексов код «10 23 45 16 47» здесь описывает указатель как «[D1+(D2+D4)*2+D4+3567]», что можно упростить до «[D1+2*D2+3*D4+3567]» следуя нехитрым школьным правилам код «10 23 45 16 47 48» показывает «[D1+(D2+D4)*2+D4+D4+35678]» повторяя D4 дважды. Интерпретируем как «[D1+((D2+D4)*2+D4)*2+D4+35678]» и получаем «[D1+4*D2+7*D4+35678]» Совсем нетрудно догадаться, что появились механизмы умножения индексных регистров через последовательно-выборочный двоичный сдвиг. Да, код при этом начинает разбухать прилично, но программист получает возможность описать указатель любой сложности. Одним словом… Эврика! Но, оказывается, здесь кроется один крошечный подвох… код «10 10» как следует интерпретировать, если индексные регистры ещё не указаны, а умножение - «[D1+(0)*2+0]» - действует? код «10 10 10» с таким механизмом это «[D1+((0)*2)*2+0]» - что это такое вообще??? Ещё месяцы ушли на решение этой концептуальной задачи. В итоге, получилось следующее: код «10 10» подавляет (сбрасывает) регистр базы и аккумулируется как «маргинал»: Условно, через шарп - «1#» код «10 10 10» образует «маргинал 1#», но указатель снова в деле. Получаем маргинальный указатель - «1#[D1+0]» код «80 80 70 70 10» уже интерпретируется как маргинальный указатель «87#[D1+0]» Тем самым, никакие исключения, в жанре Intel, в моей архитектуре не понадобились, а в сумме - появились и комплексные векторы на ячейку памяти, и «маргинализаторы операций», выводящие код инструкций «вон из процессора» - в сопроцессор… Естественно, всё это не ограничилось JavaScript-опытами (ссылка на JavaScript кодировщик инструкции). Verilog-модель вычислителя указателя с маргиналами: Имеется так же схема на Logisim под ИМС ТТЛ (незаконченная): Те же Intel в своей IA-64 не стали разбираться с корректным выполнением инструкций типа «ADD DX,AX» и вообще обнуляют старшие 32 бита RDX в таких случаях. И здесь по стопам Intel мне совсем ни к чему. Просто на аппаратном уровне такие ситуации также отлавливаются и устанавливаются ещё один флаг. К тому же, регистр A0 у меня выполняет функцию PSW: Использовать его в вычислениях бессмысленно. И регистры группы D0-D9 ссылаются либо к портам УВВ (Devices), либо к регистровым парам (как DX - это DH и DL). Если «MOV BL,BL» работает как «NOP», то мне такие «пустышки» в большом количестве не нужны - такие ситуации также отлавливаются. Как результат, шесть инструкций «ADD / SUB / AND / OR / EOR / MOV» дешифратором с учётом всех комбинаций операндов синтезируются в три десятка различных инструкций. Тем самым, к АЛУ подтягивается уже не три бита кода операции, а все 11, из-за чего программно доступно уже большее число разнообразных инструкций всех видов. Например, вот фрагмент логов отладчика дешифратора команд: M# / M - последовательность "маргиналов" VV / V - наличие корректного вектора IC - код инструкции E - флаг использования одного регистра за приёмник и за транслятор AD - флаги наличия A0/PSW или D со стороны приёмника или транслятора При прогонке отладочного кода через отладочную модель дешифратора команд выдаётся примерно такой лог: Тем самым, показывает свою работоспособность. ТЕ ЕЩЁ МАРГИНАЛЫ Как сказано выше, указание неверного указателя на аппаратном уровне обрабатываются в особенном виде, из-за чего программист может использовать эти резервные механизмы по своему усмотрению. Однако, эти самые маргиналы местами просто слишком маргинальны. Например, рассмотрим несколько странных инструкций: код «00» - команда останова «HLT»: Переход на адрес 0000 с сохранением точки останова в D0 (регистровой паре B0:C0) код «10 00» - команда останова «HLT [D1+0]»: Безусловный переход на адрес [D1] с сохранением точки останова в D0 код «10 10 00» - команда останова «HLT1» или «HLT#1»: Здесь присутствует маргинал - непонятная операция… код «10 10 10 00» - команда останова «HLT 1#D1+0» или «HLT 1#[D1+0]»: Присутствует маргинальный указатель - ещё страннее… Нетрудно заметить, что на одной только операции Останова с частной комбинацией префиксов уже довольно неясная ситуация. Даже если браться обрабатывать такие комбинации на самом высоком уровне - эмуляцией в JavaScript, не совсем понятно, что именно с этим безобразием делать… МАРГИНАЛЬНЫЕ КАНАЛЫ Так как «маргинальные индексы» временно помечались в ассемблере/дизассемблере с «решёткой-шарпом», то это напомнило некоторую аналогию с каналами в Бейсиках (у ZX-Spectrum также имеются). Так, есть такой GW-Basic - оператор OPEN - вполне хороший пример. Тем самым, вполне можно: код «10 10 00» - команду «HLT#1» можно условно обозначать за «CLOSE#1»… код «10 10 10 00» - команду «HLT#1 [D1]» можно условно обозначать за «OPEN#1 "/dev/…"»… Тем самым, у процессора на программном уровне в системе команд появляются две, хоть и побочные, но весьма любопытные операции. Программист получает основные операции работы с файловой системой уже на самом низком уровне. Сам процессор может продолжать игнорировать такие операции и ничего не делать. Тогда как внешняя периферия и т.н. сопроцессор может перехватывать все эти «маргинальные конструкции инструкций» и производить соответствующую настройку. В таком случае, если в системе будут присутствовать «сетевой сопроцессор», «графический сопроцессор» или «математический сопроцессор», уже на уровне ассемблера предоставляется довольно мощное окружение… МАРГИНАЛЬНАЯ ЭМУЛЯЦИЯ КАНАЛОВ Естественно, о поддержке «маргинального окружения» на аппаратном уровне думать не приходится в принципе! Тем самым, следует продумать механизмы прерываний, чтобы поддерживать все эти «маргинальные каналы» на программном уровне драйверов операционной системы. Ещё во времена DOS и IBM PC-XT существовали библиотеки эмуляции отсутствующего FPU. Здесь - примерно то же самое… ПРИМЕР Если мы настроили «Маргинал #1» указателем на строку, типа "/dev/ega/320x240/" как открытия непосредственной проекции EGA-графики, то код 10 10 80 90 80 80 90 80 80 80 80 80 80 A7 будет означать «MOV 1#[D8+320D9],A7», что буквально «*(BYTE *)(D8 + 320 * D9) = A7», где D8 - координата X и D9 - координата Y. Получается одна длинная операция пиксельной записи в графическую плоскость. P.S: Как можно подметить, термин «маргинал» в рамках текущих разработок используется как устоявшийся. Вполне возможно, что вместо привычных «эскейпов» его вполне можно использовать…
  7. Более-менее написал Эмулятор Процессора Койяанискаци. Работает и на смартфонах. В качестве демонстрационной среды выбрал карманную игру Автослалом (Электроника ИМ-23) с имитацией ЖК-сегментов, управляемых битами РОН D₀…₇. Эмулятор имеет ассемблер (при изменении листинга в поле необходимо затем перезагрузить страницу, чтобы те вступили в силу после ретрансляции), дамп памяти и дизассемблер. Порты Ввода-Вывода Как и сам процессор с идеологией Акына, карта портов ввода-вывода планируется акынско-сквозной, где в D₉ указывается серийный индекс периферийной микросхемы, а через D₀…₈ производится работа с выбранной микросхемой: ╔════╤════════╤════════════════════════════════════════════════════════════════╗ ║ D9 │ ИМС │ Описание доступа к ресурсам микросхемы через РОН процессора ║ ╠════╪════════╪════════════════════════════════════════════════════════════════╣ ║0x53│ i8253 │ D0/D1/D2 - Каналы Счётчиков 0/1/2 ║ ║ │К580ВИ53│ D3 - Регистр Статуса Таймера ║ ╟────┼────────┼────────────────────────────────────────────────────────────────╢ ║0x55│ i8255 │ D0/D1/D2 - Порты A/B/C ППА ║ ║ │К580ВИ55│ D3 - Регистр Статуса ППА ║ ╟────┼────────┼────────────────────────────────────────────────────────────────╢ ║0x57│ i8257 │ D0/D2/D4/D6 - Каналы 0/1/2/3: ПДП Адрес ║ ║ │ │ D1/D3/D5/D7 - Каналы 0/1/2/3: ПДП Счёт ║ ║ │К580ВТ57│ D8 - Регистр Статуса ПДП ║ ╟────┼────────┼────────────────────────────────────────────────────────────────╢ ║0x79│ i8279 │ D0 - Регистр Данных ║ ║ │К580ВВ79│ D1 - Регистр Статуса ║ ╚════╧════════╧════════════════════════════════════════════════════════════════╝ Это значит, что разработчику нет необходимости изучать какую-то карту, так как достаточно просто выбрать номенклатурный индекс конкретной микросхемы и работать уже с ней. Если в системе предусматривается, например, 250 микросхем типа i8253/К580ВИ53, то предварительно в регистр D₉ записывается код 00₁₆, а в регистр D₈ - порядковый индекс микросхемы в системе, после чего в D₉ уже записывается код 53₁₆ и выбранный таймер доступен к программированию портами D₀…₃.
  8. Написaл Кой-Машины-Эмулятор… Не шедевр, конечно. Всё сырое. Работает не очень-то стабильно. (После полной загрузки жмите ПРОБЕЛ для пошаговой эмуляции…) На дамп справа можно не обращать внимания - он для моего личного наблюдения за архитектурным состоянием РОН и всей эмуляции в целом… P.S.: На данный момент разработка/проработка концепции приостановлена…
  9. Говoрят, что Wang 2200 некогда был попыткой полностью сокрыть машинный код от пользователя. (Бейсик без PEEK/POKE - это же кошмарный ужас!) Получается, у меня - диаметрально противоположная попытка: Сделать чуть ли ни Бейсик в железе… Из 6502 позаимствовалось лишь активное обращение к ОЗУ страницы #0, где A, X и Y - походят на промежуточные регистры с программным доступом, а остальные 256 регистров - в странице #0… Сейчас упростил Дешифратор Команд до семи: HLT - та же операция INT #0, но отдельно BCD - не «Binary Coded Decimal» режим, а «Byte-Code-Data» - аналог x86-imm8 констант, но работающих как 99 префиксов ALU - группа стандартных операций АЛУ REG - активация регистров-аккумуляторов в каждой из четырёх групп ARG - связывание регистровых групп в связку операндов АЛУ CND - группа условных операций INT - группа программных прерываний (от 32 до 3200 векторов) В основном, упразднил операции EXT/FIX для чтения/записи ОЗУ: Теперь доступа к ячейкам ОЗУ прямого нет - только через префиксы. 00|HLT ; Останов / INT #0 0C|AND Ri,T₀ ; Conjunction 3D|OR Ri,T₃ ; Disjunction A1|REG A₁ ; Активация аккумуляции в A₁ BC|ARG B,C ; Операнды B и C как аргументы АЛУ-операций CE|RET NC ; Возврат, если NOT CF CF|RET CF ; Возврат, если CF E1|INT 0xE100 ; Прерывание на вектор E100₁₆ 21 00|INT 0x0021 ; Прерывание на вектор 0020₁₆ с опцией #1 21 0C|AND Ri,[B₂C₂+1] ; Конъюнкция с ячейкой памяти 21 3D|OR Ri,[B₂C₂+1+T₃] ; Дизъюнкция с ячейкой памяти 21 A1|MOV [B₂C₂+1],A₁ ; Запись в память значения регистра 21 BC|<<<reserved>>> ; 1584 комбинации пока не определены 21 CE|JNC $__21 ; Переход на позицию 21₁₆ текущего параграфа, если NOT CF 21 CF|JCF $__21 ; Переход на позицию 21₁₆ текущего параграфа, если CF 21 E1|INT 0xE120 ; Прерывание на вектор E120₁₆ с опцией #1 43 21|<<<reserved>>> ; 9801 комбинация пока не определена Выглядит более-менее исчерпывающе и гибко, чтобы реализовывать любые алгоритмы. B9 C9 03 BC|DEC B₉C₉,3 ; Экспериментальный вариант - декремент пары 90 A1|MOV [B₉C₉+0],A₁ ; Так как PUSH отсутствует, программно реализуем 91 A2|MOV [B₉C₉+1],A₂ ; Каждый регистр записываем в буфер стека 92 C7|MOV [B₉C₉+2],C₇ ; на свою позицию CC C7 92 0F|MOV C₇,[B₉C₉+2] ; Так как POP отсутствует, программно реализуем AA A2 91 0F|MOV A₂,[B₉C₉+1] ; Каждый регистр читаем из буфера стека A1 90 0F|MOV A₁,[B₉C₉+0] ; со своей позиции B9 C9 03 CB|INC B₉C₉,3 ; Экспериментальный вариант - инкремент пары P.S.: Когда пытался программировать на NES под 6502, сложилось мнение, что 6502 - RISC-архитектура, что являлось заблуждением. Тем самым, признаю собственную некомпетентность в различных архитектурах и понимаю, что Койяанискаци, хоть и задумывался 2 года назад как шуточный шаг в сторону RISC, но теперь он неплохо отдаёт привкусом CISC…
  10. Написал Вторую статью с пошаговой иллюстрацией проектирования Койяанискаци, так как начал проектирование с нуля… Сейчас я полностью игнорирую моменты, связанные с производительностью. Регистровый Файл вынес в память, аналогично как в MOS 6502… Операция АЛУ занимает целых 5 тактов: Цикл M₀ читает регистр A₀ с PSW-словом Цикл M₁ читает регистр правого операнда Цикл M₂ читает регистр-приёмника левого операнда Цикл M₆ сохраняет результат в ячейку регистра-приёмник Цикл M₉ сохраняет PSW-слово Иными словами, операция «A₁ += B₂» разворачивается в: M₀: Load PSW:[0x00A₀] M₁: Load SRC:[0x00B₂] M₂: Load DST:[0x00A₁] M₆: Save [0x00A₁]:DST+SRC M₉: Save [0x00A₀]:PSW И требует 1+5=6 тактов, где 1 такт - на считывание кода самой операции, а ещё 5 - на все те циклы… P.S.: Тем самым, за производительностью не гонюсь, но хочу попробовать получить эскиз Койяанискаци с наименьшим количеством элементов, если браться всё-таки его делать рассыпухой на ТТЛ…
  11. B общем, из-за жёстких, почти аномальных проблем с ПК, я до сих пор не мог полноценно пользоваться своей техникой… Только гуглом в режиме read-only на смартфоне (ни один пароль свой не помню)… От скуки купил Raspberry Pi 4 и обнаружил, что и HDMI-вход пятого монитора вылетел, так как на телевизоре сестры RetroPie запустился и отобразился, а мой монитор лишь VGA принимает и пишет, что на HDMI нет сигнала. Причём при старте самого ПК заставка BIOS по HDMI передаётся в сильных шумах красными оттенками и с сильным мерцанием. Шнуры менял - не помогало. (Раньше, когда показывал ещё, при любой помехе от холодильников или ЛДС монитор часто гас на пару секунд, пока вообще через год не потерял сигнал…) По теме… Пока сутками на карантине пролёживал диван, мысленно продумывал свой процессор… Как я уже говорил, архитектура задумывалась как наращиваемая по разрядности. На данный момент комбинация кода «40 A5» или «80 AB» бессмысленна, так как выбор регистра «A5» под префиксом «40» просто вызывает задержку в цикле, где просто регистр 4 раза выбирается. Но, есть мысль несколько усложнить дешифратор команд на предмет проверки наличия префикса. Тогда «A5» и «40 A5» станут разными командами. Появится перспектива работать с упакованными данными, где «40 A5» сменит режим регистра A₅ на 32-битный… «AB 10 A5 3A» - эквивалент «PADDB A5,B3» «AB 20 A5 3A» - эквивалент «PADDW A5,B3» «AB 40 A5 3A» - эквивалент «PADDD A5,B3» «AB 80 A5 3A» - эквивалент «PADDQ A5,B3» Тем самым, если взять вариант данного процессора с 64 битами, то операции АЛУ будут всегда подобны SIMD-вычислениям и можно производить операции над упакованными байтами, словами и двойными словами… Если «40 A5» изменяет «режим упаковки» отдельного регистра A₅, то «40 AB» должен как-то менять режим взаимодействия операндов «A,B». Но за все эти месяцы я так и не придумал, как именно… Хотя, есть идея использовать их как отдельные служебные режимы, где «40 AD» означает 32-битный режим AD'ресации… Адресация В архитектуре любой разрядности ячейки памяти адресуются формулой «(Bi << 8) + Ci». Тем самым, как и в x86-процессоре, регистры B выступают за сегмент, а C - смещение. Так, в 8-битном классическом исполнении адресация получается 0…FFFF, а в 16-битном - 0…100FEFF. Это позволит запускать 8-битный код, скажем, на 32-битном варианте процессора. Так как префиксами «10 B9» можно указать, что сегмент - 8-битный и адресует до 65536 ячеек.
  12. Нe по теме… Пo теме… Вроде бы нашёл способ организации Регистрового Файла без выкрутасов, но только ментально. Код ещё никакой не писал… Важно/!\ Так как в состав большинства FPGA входят несколько множителей не требующих тактовой частоты, а Хабр ещё и подтверждает это, то появились некоторые соображения… Сейчас имеются такие команды: A1 AC 3A|ADD A1,C3 ; A1 += C3 A1 AC 50 3A|ADD A1,5*C3 ; A1 += 5 * C3 // префикс 50 устанавливает коэффициент умножения A1 AC 3B|SUB A1,C3 ; A1 -= C3 A1 AC 50 3B|SUB A1,5*C3 ; A1 -= 5 * C3 // префикс 50 устанавливает коэффициент умножения Но есть мысли немного доработать: A1 AC 3A|ADD A1,C3 ; A1 += C3 A1 AC 50 3A|MULU B5:C5,A1,C3 ; B5C5 = uint(A1 * C3) // префикс 50 выбирает индекс приёмников произведения A1 AC 3B|SUB A1,C3 ; A1 -= C3 A1 AC 50 3B|MULI B5:C5,A1,C3 ; B5C5 = int(A1 * C3) // префикс 50 выбирает индекс приёмников произведения Тем самым, появится операция умножения порядковых и целочисленных… Если придерживаться ТТЛ-исполнения, К555ИП9 выполняет функцию перемножения, а сам Fetch-узел в его нынешнем исполнении на цикле M3, предназначенном для обмена данными с ОЗУ, можно растягивать на необходимое число тактов. Тем самым, на чистом ТТЛ операция умножения потребует до 24 тактов, что не слишком плохо… Естественно, в Verilog под FPGA задержка нужна для сохранения результата в две секции регистров: Младший байт результата в регистр C и старший байт результата в регистр B… Тем самым, на FPGA код будет множить уже за 4 такта… P.S.: Напомню, что Койяанискаци, как RISC-ядро для выполнения CISC-инструкций, не задумывался для подобных мощных вычислительных операций. Тогда как ПЛИС вполне позволяет вводить подобную арифметику. Вопрос лишь в одном: А стоит ли? Концептуальная дилемма…
  13. Обнoвил эмулятор… Сейчас он переводит машинный код прямо в JavaScript-записи. И это - уже что-то, так как кое-что уже проявляется и нет абсолютного вакуума дампа для не набитого глаза… Просто жмёте «Step» и шагаете по кодам. В момент «прыжка» листинг обновляется сам. В таблице регистров красным помечаются приёмники, а зелёным - источники…
  14. Пoка с эмулятором проблема. Он вроде бы и работает, но дизассемблер не получается как надо… Так, код «AB A1 2B 3D 4E» отображает как «A1 = ((((A1) - B2) | B3) ^ B4» и выполняется через eval… Но, скобки «(A1)» напрягают эстетически и нужно парсер переписывать. То есть, хочу сразу добиться того, чтобы «EC» отображался как «if(CF)» с последующим блоком «{…}». Как Вы уже поняли, опять заморочился с эстетикой, а не точностью эмуляции. Первый вариант эмулятора был через «reg(into, alu(opcode, reg(into), reg(from)))». Но теперь решил кодировать всё насквозь JavaScript-выражениями. Тем самым, если написать игру Койяанискаци-кодом, то она на выходе будет чисто на JavaScript и эмулятор не потребуется вообще. Кажется, уже писал: Без проблем доставляют кеды, футболки, штаны, ушки, очки, экшн-камеры и т.п… А вот PS/2-Y штаны - не доехали. На рынке купил ATtiny-85A, но он такой маленький - меньше спичечной головки, что непонятно, каким поялом к нему подходить: Убрал глубоко в шкаф в коробок, чтобы не посеять… Очень туго - с ЭСЛ-серией К500 и К1500: Всё на драгмет скупили представители соседней республики… А так, Dallas 1-Wire термодатчики и регистры - может и есть, но адаптеров USB<->1-Wire - нету… Вернее, достать можно и серию К134, но через заказ, барыг и переплату…
  15. Вoт именно! Койяанискаци - не цель, а побочный продукт моих любительских изысканий… Типа «Везде полным полном всяческих уроков, как правильно делать процессор. Возьму-ка я Atanua и состряпаю процессор так, как я понимаю… Нет, Atanua не так удобен, а Logisim более дружелюбен…». То есть, скука + тестирование интерфейсов различных программ (KiCAD все советовали). Вот KiCAD немного суровее против Atanua и Logisim - я на нём запарился сложную схему строить. OrCAD - много детских болезней GUI и рисование схемы приводит к зависанию и закрытию всего. Хуже того: Не всегда восстановление работает! Но KiCAD я этой зимой заменил на Proteus, так как отличаются по освоению незначительно, а функционально - существенно! (В OrCAD/KiCAD при запуске симуляции/топологии открываются отдельные программы, в Proteus - всё интегрировано, как в TurboPascal 7!) Тем самым, более менее освоился в нескольких пакетах и попутно получился процессор… P.S.: Тем более с моей профессиональной деятельностью по специализации эти всякие ТТЛ и FPGA никак не пересекаются…
×
×
  • Создать...