Як мови вплинули на дизайн процесора? [зачинено]


44

Нам часто кажуть, що апаратне забезпечення не хвилює, на якій мові написана програма, оскільки вона бачить лише скомпільований двійковий код, однак це не вся правда. Наприклад, розгляньте скромну Z80; його розширення до набору інструкцій 8080 включають такі інструкції, як CPIR, які корисні для сканування рядків у стилі C (з NULL), наприклад для виконання strlen(). Дизайнери, мабуть, визначили, що запуск програм C (на відміну від Pascal, де довжина рядка знаходиться в заголовку) - це те, для чого, ймовірно, використовувався їх дизайн. Ще один класичний приклад - машина Lisp .

Які ще приклади є? Наприклад, інструкції, кількість та тип регістрів , режими адресації, завдяки яким певний процесор надає перевагу умовам певної мови? Мене особливо цікавлять ревізії тієї ж родини.


3
Не забувайте, що на Z-80 також була інструкція LDIR, дуже корисна при копіюванні рядків, коли ви знаєте довжину (наприклад, у Pascal, де довжина зберігалася у заголовку).
TMN

27
1. Z-80 був розроблений в 1975 році, коли Unix і C були незрозумілою операційною системою та мовою на кількох комп'ютерах, за 3 роки до першого випуску K&R. 2. Немає нічого про Паскаля, який зобов'язує довжину рядка бути "в заголовку". 3. Рядки CP / M, основної операційної системи мікрокомп'ютера на той час, були припинені символом "$", а не "\ 0". CPIR може шукати будь-якого персонажа. 4. CPIR узгоджується з CPDR (пошук назад), а також іншими інструкціями -IR та -DR. Висновок: CPIR не має нічого спільного з мовою програмування C. Це просто інструкція пошуку в байтах.
librik

4
Найбільшою (і однією з найприємніших для дизайнерів обладнання) речей, вимушених C, є байт-адресація. Процесори були б простішими та швидшими без цієї гидоти.
SK-логіка

1
@ SK-логіка: Хоча стандарт POSIX вимагає байтової адреси, стандарт C цього не робить. Будь-яка реалізація, в якій sizeof(int)дорівнює 1, вимагає charпідписати цей тип (оскільки атрибут intповинен вміщувати всі значення типу char). Я написав код для машини, де charі intобидва є 16-бітовими цілими числами; Найбільші труднощі полягають у тому, що не можна використовувати об'єднання для перетворення типів, а ефективне зберігання великої кількості байтів вимагає упаковки та розпакування вручну. Ці питання незначні порівняно з можливістю в C, що sizeof (int) == sizeof (long), оскільки ...
supercat

2
... це означає, що немає стандартного типу, який гарантовано містить різницю між двома unsigned intзначеннями. C99 покращив цю ситуацію, але до початку C99 не існувало гарантовано-безпечного однокрокового способу порівняння потенційно-негативного значення зі значенням типу unsigned int(треба було б перевірити, чи було число негативним, перш ніж проводити порівняння).
supercat

Відповіді:


20

Існуючі відповіді фокусуються на змінах ISA . Є й інші апаратні зміни. Наприклад, C ++ зазвичай використовує Vtables для віртуальних дзвінків. Починаючи з Pentium M , Intel має компонент "непрямого прогнозування гілок", який прискорює виклики віртуальних функцій.


6
І архітектура Berkeley RISC включала поняття "файл реєстру", тож замість того, щоб функції "розливати" регістри на стек, було надано блок з 8 регістрів кожній функції. Це значно прискорило об'єктно-орієнтований код, оскільки воно, як правило, складається з багатьох викликів методів до коротких методів.
TMN

1
Це не дійсний приклад. Дизайн "Таблиця функціональних покажчиків" також використовується у багатьох динамічних сценаріях зв'язування, наприклад, через імпорт та експорт DLL в Windows, а також використовується в програмах C. Хоча я думаю, ви можете стверджувати, що він показує, що процесор оптимізований для конкретного використання, це не залежить від мови.
DeadMG

@DeadMG: Інші випадки виграли, це правда. Але поки C ++ не став популярним, на дизайни процесора не впливали . І це було поставлене питання. Аналогічно, TMN має пункт про файли реєстру. Асамблея не мала такої чіткої концепції функцій. Функції, як ми їх часто розуміємо сьогодні, відносяться до Algol 60, і тому ми можемо сказати, що Algol 60 вплинув на дизайн файлів реєстру процесора.
MSalters

14

Набір інструкцій Intel 8086 включає в себе варіацію "ret", що додає значення покажчику стека після проскакування повернення адреси. Це корисно для багатьох реалізацій Pascal, коли абонент функції висуне аргументи на стек перед тим, як здійснити виклик функції, і виводить їх після цього. Якщо рутина прийме, наприклад, параметри вартості чотирьох байтів, вона може закінчитися "RET 0004" для очищення стека. Якщо відсутня така інструкція, така конвенція виклику, ймовірно, вимагала б, щоб код попівав зворотну адресу до реєстру, оновив покажчик стека та перейшов до цього реєстру.

Цікаво, що більшість кодів (у тому числі підпрограми ОС) на оригінальному Macintosh використовували конвенцію Pascal для викликів, незважаючи на відсутність спрощеної інструкції в 68000. Використання цієї конвенції про виклики зберегло 2-4 байти коду на типовому сайті виклику, але вимагало додаткової 4-6 байт коду на місці повернення кожної функції, яка приймала параметри.


Є також ENTERаналог цього RET n...
herby

1
@herby: я не думаю, що ENTERіснували в оригіналі 8086; він прийшов з пізнішими процесорами. Однак це викликає цікавий момент: режими адресації на основі ВР чітко розроблені навколо використання складених параметрів та місцевих жителів, доступ до яких здійснюється за допомогою покажчика кадру. Я вважаю цю конвенцію цікавою у багатьох напрямках, особливо враховуючи, що (1) чистий код мови збірки є більш сприятливим для використання значень у регістрах, ніж стек, але (2) переваги [BP + nn] адресації над [SP + nn] адресація є більш важливою для програм мовної збірки, які отримують доступ до речей на стеку, ніж ...
supercat

... для рукописного монтажного коду. Компілятор, як правило, знатиме для кожної створеної інструкції порівняння SP та BP; якщо SP - це BP-8, наприклад, компілятор насправді не простіший адресувати [BP + 12], ніж [SP + 20]. Якщо під час перекомпіляції компілятору необхідно додати ще один PUSH / POP навколо блоку коду, він може коректувати зрушення на основі SP. З іншого боку, в рукописній збірці для додавання PUSH / POP швидше за все потрібно буде встановити код між ними. Тож покажчики кадрів є переважно користю для комбінованого коду високого рівня / asm.
supercat

Можливо, можливість повторного використання коду без його перекомпіляції також є деякою граничною точкою використання для адреси BP. І Бог знає, чи інструкції щодо адресації ВР у схемі не швидші, ніж відповідні SP, оскільки адресація BP - це як стандарт ...
herby

3
@herby: Насправді, я підозрюю, що велика частина причин, що компілятори зазвичай використовують покажчики кадрів, має багато спільного з налагодженням. Для налагодження програми, яка не використовувала таку конвенцію, вимагатиме, щоб компілятор генерував - і налагоджувач використовував - файл із переліком зсуву SP-BP для кожної інструкції. Такі детальні метадані є поширеними сьогодні (і є невід'ємною частиною того, що робить мови, зібрані сміттям практичними), але необхідна кількість оперативної пам'яті була б неприйнятною 30 років тому.
supercat

10

Один із прикладів - MIPS, який має як addі adduдля захоплення та ігнорування переповнення відповідно. (Також subі subu.) Потрібна була перша інструкція для таких мов, як Ada (я думаю - я ніколи фактично не використовував Ada), які явно мають справу з переповненнями, а другий тип для таких мов, як C, які ігнорують переповнення.

Якщо я пам'ятаю правильно, фактичний процесор має деяку додаткову схему в ALU для відстеження переливів. Якби єдиною мовою, про яку люди піклувались, була С, це не потребувало б.


Не впевнені, що пов’язані, але ці інструкції, ймовірно, також корисні в інших ситуаціях, наприклад, безпечному розподілі пам’яті, тобто, якщо ви виділяєте nmemb*size+offsetбайти і вам потрібно переконатися, що ви не отримаєте переповнення.
NikiC

@NikC: Я думав, що adduта subuінструкції (ті, які не перевіряють на переповнення), були ті, які були додані, щоб зробити C щасливим. Звичайно, я не знаю насправді - ми лише розпливчасто висвітлювали це на лекції, і я, звичайно, не знаю архітектури: P.
Тихон Єлвіс

О так, я думав навпаки, вибачте: /
NikiC

8

Серія Burroughs 5000 була розроблена для ефективної підтримки ALGOL, а iAPX-432 від Intel був розроблений для ефективного виконання Ada. У комп’ютера Inmos була своя мова, Occam. Я думаю, що процесор Parallax "Propeller" був розроблений для програмування з використанням власного варіанту BASIC.

Це не мова, але набір інструкцій VAX-11 має єдину інструкцію для завантаження контексту процесу, який був розроблений після запиту дизайнерської команди VMS. Я не пам’ятаю деталей, але ISTR знадобилося стільки інструкцій, щоб здійснити серйозну верхню межу кількості процесів, які вони могли запланувати.


Що з цих конструкцій робить їх особливо придатними? Наприклад, від якої особливості iAPX особливо користь?
Гай

ISTR, що ціль Ada в iAPX-432 більше намагалася врятувати невдалу конструкцію, приєднавши її до чогось із ще великими сподіваннями, ніж будь-що інше.
AProgrammer

@AProgrammer: Я майже впевнений, що iAPX-432 розроблявся з самого початку для використання Ada. Я навіть згадую деякі чутки про те, що Intel не збирається публікувати набір інструкцій, щоб відмовити програмуванню мови монтажу та змусити людей використовувати Ada для всього.
TMN

1
@TMN, проект 432 від Intel розпочався у 1975 році та був представлений у 1981 році (Вікіпедія). Ironman (остаточні вимоги до Ада) був опублікований у січні 1977 р., А зелений був обраний у травні 1979 р., Модифікований, а остаточний результат опублікований як військовий стандарт у липні 1980 р. Існує проблема часової шкали, в якій зазначається, що iAPX-432 був розроблений з початок використання Ада. (Це пізній і типовий процесор "закрити семантичний пробіл" зі звичайними недоліками в той час, коли альтернативи почали шукати; маркетинг його як процесор Ada був попереднім для збереження невдалої конструкції - ISTR, що ніхто, крім Intel, не використовував його )
AProgrammer

1
@AProgrammer: Хм, схоже, ти маєш рацію. Я зіткнувся з цим документом від головного архітектора 432, і в резюме він говорить: "Ця тісна відповідність архітектури та мови не відбулася, тому що 432 був розроблений для виконання Ада - це не було". Мені доведеться викопати свою стару книгу 432 і подивитися, що там написано.
TMN

8

Одне, про що, мабуть, ніхто не згадував поки що, це те, що досягнення оптимізації компілятора (де основна мова значною мірою не має значення) призвело до переходу від наборів інструкцій CISC (які значною мірою були розроблені для кодування людьми) до наборів інструкцій RISC (які були значною мірою призначений для кодування компіляторами.)


5

Сімейство Motorola 68000 представила деяку автоматичну посилку адреси, що зробило копіювання даних через процесор дуже ефективним та компактним.

[Оновлений приклад]

це був якийсь код C ++, який вплинув на асемблер 68000

while(someCondition)
    destination[destinationOffset++] = source[sourceOffset++]

реалізований у звичайному асемблері (псевдокод, я забув команди асемблера 68000)

adressRegister1 = source
adressRegister2 = destination
while(someCondition) {
    move akku,(adressRegister1)
    move (adressRegister2), akku
    increment(adressRegister1, 1)
    increment(adressRegister2, 1)
}

з новим адресом способу це стало щось подібне

adressRegister1 = source
adressRegister2 = destination
while(someCondition) {
    move akku,(adressRegister1++)
    move (adressRegister2++), akku
}

лише дві інструкції на цикл замість 4.


1
Як на це вплинули конвенції певної мови?
Гай

дивіться оновлений приклад
k3b

Ага, нагадує мені про оптимізацію циклу DBxx в 68010.
Гай

7
Насправді, я думаю, у вас це є назад. Автоматичне [в | де] створення крементування було частиною набору інструкцій PDP-11, що, ймовірно, вплинуло на дизайн C.
TMN

5

Мейнфрейм серії IBM Z є нащадком IBM 360 з 1960-х років.

Там було розроблено кілька інструкцій, щоб спеціально прискорити програми COBOL та Fortran. Класичний приклад BXLE- "Відділення на індекс низький або рівний", який є більшою частиною фортранського forциклу або COBOL, PERFORM VARYING x from 1 by 1 until x > nінкапсульованої однією інструкцією.

Існує також ціла родина упакованих десяткових інструкцій для підтримки десяткової арифметики з фіксованою точкою, поширеної в програмах COBOL.


Я думаю, ти маєш на увазі нащадка .
Завод-Муза

@ X-Zero - ой! Ранковий ранок, недостатньо кофеїну в системі тощо .......
Джеймс Андерсон

1
Більш цікавою є інструкція щодо повторного блоку TI 32050 DSP. Його операнд - це адреса інструкції, що слідує за останньою в циклі; завантаження реєстру підрахунку циклу та виконання інструкції повторення блоку призведе до того, що вказівки до (але не включаючи) цілі будуть повторені вказану кількість разів. Дуже сильно нагадує DOпетлю FORTRAN .
supercat

@supercat Кожен DSP, гідний імені, включає три особливості: нульовий накладний цикл, одиночну інструкцію множення-накопичення та дещо зворотний режим адресації. Практично кожен алгоритм DSP, відомий Man, використовує петлі. Два найпоширеніші алгоритми - це фільтр FIR, який представляє собою цикл навколо множини-накопичення, і FFT, для якого біт-зворотна адресація є критичною. Багато DSP включають в себе операцію «Метелик» FFT Radix-2 або подвійне множення / додавання, яке може бути використане для створення метелика з однією інструкцією.
Джон Р. Стром

@ JohnR.Strohm: Кожен DSP, який я бачив, включає повторне множення-накопичення, але не всі вони включають більш узагальнені нульові петлі. Насправді я не зовсім впевнений, чому такі петлі слід вважати лише функцією "DSP", оскільки вони також будуть корисні для багатьох "звичайних процесорних" кодів.
supercat

3

Ранні процесори Intel мали такі функції, багато з яких зараз застаріли в 64-бітному режимі:

  • Вказівки ENTER, LEAVE та RET nn [ранні посібники, чітко сказані, були введені для структурних мов блоків, наприклад, Pascal, який підтримує вкладені процедури]
  • інструкції щодо прискорення арифметики BCD (AAA, AAM тощо); також підтримка BCD у x87
  • Інструкції JCXZ та LOOP щодо реалізації відлічених циклів
  • INTO, для створення пастки на арифметичному переливі (наприклад, в Ада)
  • XLAT для пошуку таблиць
  • БУНД для перевірки меж масиву

Прапор знака, знайдений в реєстрі статусу багатьох процесорів, існує для того, щоб легко виконувати арифметичні підписані та неподписані.

Набір інструкцій SSE 4.1 вводить інструкції для оброблення рядків, як підраховані, так і з нульовим завершенням (PCMPESTR тощо)

Крім того, я міг уявити, що ряд функцій на рівні системи був розроблений для підтримки безпеки компільованого коду (перевірка обмеження сегмента, ворота викликів з копіюванням параметрів тощо)


3

Деякі процесори ARM, в основному, це мобільні пристрої, включають (d) розширення Jazelle, яке є апаратним перекладачем JVM; він інтерпретує байт-код Java безпосередньо. JVM, обізнаний з Jazelle, може використовувати апаратне забезпечення для прискорення виконання та усунення значної частини JIT, але резервна версія програмного забезпечення VM все ще забезпечується, якщо байтовий код не може бути інтерпретований на мікросхемі.

Процесори з таким блоком включають інструкцію BXJ, яка переводить процесор у спеціальний режим "Jazelle", або якщо активація блоку не вдалася, вона просто інтерпретується як звичайна гілка інструкція. Пристрій повторно використовує регістри ARM для утримання стану JVM.

Наступником технології Jazelle є ThumbEE


2

Наскільки мені відомо, це було більш поширеним у минулому.

Є сесія запитань, в якій Джеймс Гослінг сказав, що люди намагаються зробити апаратне забезпечення, яке б краще справлялося з байт-кодом JVM, але тоді ці люди знайдуть спосіб зробити це із загальним "загальним" Intel x86 (можливо, компілюючи байт-код якось розумним чином).

Він зазначив, що в використанні загального популярного чіпа (наприклад, Intel) є перевага, оскільки велика корпорація кидає на продукт величезні суми грошей.

Відео варто переглянути. Про це він говорить у 19 чи 20 хвилини.


2

Я зробив швидкий пошук сторінки, і, здається, ніхто не згадав процесор, розроблений спеціально для виконання Forth . Мова програмування Forth заснована на стеках, компактна і використовується в системах управління.


2

Процесор Intel iAPX був спеціально розроблений для мов OO. Не дуже вийшло, хоча.

IAPX 432 ( Intel Advanced Processor архітектура ) був перший 32-розрядний мікропроцесор Intel, дизайн, введений в 1981 році у вигляді набору з трьох інтегральних схем. Він був розроблений головним дизайном Intel в 1980-х роках, що реалізує багато передових функцій багатозадачності та управління пам'яттю. Тому дизайн був названий Micromainframe ...

IAPX 432 був "розроблений для програмування повністю на мовах високого рівня" , причому Ada є основним і підтримує об'єктно-орієнтоване програмування та збирання сміття безпосередньо в апаратному та мікрокодовому кодах . Пряма підтримка різних структур даних також мала на меті забезпечити реалізацію сучасних операційних систем для iAPX 432, використовуючи набагато менший програмний код, ніж для звичайних процесорів. Ці властивості та особливості призвели до апаратного та мікрокодового дизайну, який був набагато складнішим, ніж більшість процесорів епохи, особливо мікропроцесорів.

Використовуючи напівпровідникову технологію свого часу, інженери Intel не змогли перетворити дизайн у дуже ефективну першу реалізацію. Поряд з відсутністю оптимізації в передчасному компіляторі Ada, це сприяло досить повільним, але дорогим комп'ютерним системам, виконуючи типові орієнтири приблизно на 1/4 швидкості нового чіпа 80286 на тій же тактовій частоті (на початку 1982 року).

Цей початковий розрив у продуктивності та порівняно низькій ціні 8086-ліній був, мабуть, основною причиною того, що план Intel замінити останній (пізніше відомий як x86) на iAPX 432 не вдався. Хоча інженери бачили шляхи вдосконалення дизайну нового покоління, архітектура iAPX 432 Capability тепер почала розглядатися скоріше як накладні витрати, а не як спрощуюча підтримка.

Проект iAPX 432 став комерційним провалом для Intel ...


Читаючи документ, здається, що багато аспектів дизайну можуть бути корисні в об'єктно-орієнтованих рамках, таких як сьогодні популярні. Архітектура, яка використовувала комбінацію 32-бітного ідентифікатора об'єкта та 32-бітного зміщення, у багатьох випадках може запропонувати кращу ефективність кешування, ніж та, де ідентифікатори об'єкта були всіма 64 бітами (у більшості випадків додаток, який використовував би мільярди об'єктів, краще обслуговувати, замість того, щоб мати більше, більших; один, який би зберігав мільярди байтів в одному об’єкті, був би краще поданий підрозділенням на більш дрібні об'єкти.
supercat

1

У 68000 був MOVEM, який найбільше підходив для виштовхування декількох регістрів на стек в одній інструкції, що очікується багатьма мовами.

Якщо ви бачили MOVEM (MOVE Multiple), що передує JSR (Jump SubRoutine) протягом усього коду, тоді ви, як правило, знали, що маєте справу з кодом, який відповідає C.

MOVEM дозволений для автоматичного збільшення регістру призначення, що дозволяє кожному використанню продовжувати укладку в пункт призначення, або видаляючи зі стека у разі автоматичного зменшення.

http://68k.hax.com/MOVEM


1

AVR-архітектура Atmel повністю розроблена з нуля, щоб вона була придатною для програмування в C. Наприклад, ця додаткова записка детальніше розроблена.

IMO це тісно пов'язане з відмінною відповіддю rockets4kids , з ранніми PIC16-х розробляються для прямого програмування асемблера (загалом 40 інструкцій), а пізніші сім'ї націлюються на C.


1

Коли був спроектований чисельний співпроцесор 8087, мови досить звично проводити всю математику з плаваючою комою, використовуючи тип найвищої точності, і лише округлювати результат, щоб знизити точність при призначенні його змінної нижчої точності. Наприклад, у вихідному стандарті C, наприклад, послідовність:

float a = 16777216, b = 0.125, c = -16777216;
float d = a+b+c;

сприятиме aі bдо double, додати їх, заохочувати cдо double, додайте його, а потім зберегти результат округляється до float. Хоча в багатьох випадках компілятор міг би генерувати код, який би виконував операції безпосередньо на тип float, було простіше мати набір підпрограм з плаваючою комою, які оперуватимуть лише для типу double, а також підпрограми для перетворення в / з float, ніж мати окремі набори підпрограм для обробки операцій на floatта double. 8087 був розроблений таким підходом до арифметики, виконуючи всі арифметичні операції з використанням 80-бітового типу з плаваючою комою [80 біт, ймовірно, вибрали тому, що:

  1. У багатьох 16- та 32-бітних процесорах швидше працювати з 64-бітовою мантісою та окремим експонентом, ніж працювати зі значенням, яке розділяє байт між мантісою та експонентом.

  2. Дуже важко проводити обчислення, які точні до повної точності числових типів, які використовуються; якщо хтось намагається, наприклад, обчислити щось на зразок log10 (x), то простіше і швидше обчислити результат, який точно в межах 100ulp 80-бітного типу, ніж обчислити результат, точний в межах 1ulp 64-бітового типу тип, і округлення першого результату до 64-бітної точності призведе до отримання 64-бітного значення, яке є більш точним, ніж останнє.

На жаль, майбутні версії мови змінили семантику того, як повинні працювати типи з плаваючою комою; в той час як семантика 8087 була б дуже приємною, якби мови підтримували їх послідовно, якби функції f1 (), f2 () тощо поверталися float, багато авторів-компіляторів взяли б на себе, щоб зробити long doubleпсевдонім для 64-бітного подвійного типу а не 80-бітний тип компілятора (і не передбачають інших засобів створення 80-бітних змінних), а також довільно оцінювати щось на кшталт:

double f = f1()*f2() - f3()*f4();

будь-яким із наступних способів:

double f = (float)(f1()*f2()) - (extended_double)f3()*f4();
double f = (extended_double)f1()*f2() - (float)(f3()*f4());
double f = (float)(f1()*f2()) - (float)(f3()*f4());
double f = (extended_double)f1()*f2() - (extended_double)f3()*f4();

Зауважте, що якщо f3 і f4 повертають ті ж самі значення, що і f1 і f2, відповідно, початковий вираз повинен чітко повернути нуль, але багато з останніх виразів можуть не робити. Це призвело до того, що люди засуджували "додаткову точність" 8087 року, хоча остання формулювання, як правило, перевершує третю і - з кодом, який використовує розширений подвійний тип, - рідко буде неповноцінним.

У наступні роки Intel відповіла на мовну (нещасну IMHO) тенденцію до примушення проміжних результатів до окружності до точності операндів, розробляючи їх більш пізні процесори, щоб сприяти такій поведінці, на шкоду коду, який би виграв від використання вищих точність на проміжних розрахунках.


Зауважте, що ви вже отримали відповідь ( вище ) у цій публікації. Чи є вони відповідями, які можна / слід об'єднати в одну?

@MichaelT: Я не думаю, що так - один охоплює дизайн стека, а другий - семантику з плаваючою комою.
supercat

Просто переконуюсь. Особисто я вважаю, що можна було б зробити одну, сильнішу відповідь (використовуючи заголовки для розділення розділів), але це моє питання. Ви можете все-таки використовувати заголовки, щоб чітко визначити вгорі, на що адресована кожна частина відповідей ( ## How the stack changed the processorі ## How floating point changed the processor), щоб люди могли отримати її належний розум під час її читання, і менше шансів вважати, що ви або не роздумували над відповіддю чи перестановкою однакові (r подібні) відповіді.

@MichaelT: Два відповіді досить непересічні, що я думаю, що за них слід голосувати окремо. Хоча 80486 поглинав функції, які раніше виконував 8087/80287/80387, 8086 та 8087 були розроблені як окремі мікросхеми з майже незалежними архітектурами. Незважаючи на те, що обидва запускали код із загального потоку інструкцій, цим було оброблено, якщо 8086 розглядає певні послідовності байтів як запити для генерування запитів читання / запису адреси при ігноруванні шини даних, а 8087 ігнорує все, що відбувається.
supercat
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.