Как устроен компьютер? Часть 10 (Как работат периферия)

Вуаля!

Контрольная секция нашего процессора готова! В результате, мы можем разместить в ОЗУ инструкции, а внутренности процессора смогут их выполнять. Теперь можно рассмотреть полную схему контрольной секции:

Да, это выглядит сложно, но это лишь на первый взгляд, ведь мы уже рассмотрели все части схемы в отдельности. Единственное, что добавилось в полной схеме, это несколько вентилей ИЛИ, так как к большинству битов ‘e’ и ‘s’ подключается сразу несколько проводов.

Байт, поступая в IR, вызывает определенную активность внутри схемы. Каждый возможный паттерн вызывает другую активность. В итоге, мы имеем код, в котором каждый из 256 возможных вариантов вызывает определенную деятельность в процессоре.

Эти 256 вариантов байта в IR и есть машинный код, который понимает компьютер. С помощью машинного кода мы можем заставить компьютер делать что-то полезное.

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

Еще пару слов об арифметике

На самом деле, до сих пор, из арифметики мы, пока, видели только сложение. Сильно углубляться в эту тему мы не будем, но некоторые арифметические действия все же рассмотрим. Например, вычитание можно произвести с помощью сумматора и инвертора. Допустим, мы хотим вычесть R1 из R0. Сначала мы должны инвертировать R1. Затем прибавить 1 к R1. Затем нужно сложить R0 и R1. Рассмотрим на примере 37-21:

Последний шаг 37 + 235. В ответе должно быть 272, но регистр 8-ми битный, поэтому сумматор включает бит переноса и восемь оставшихся бит – это 00010000, что в десятичной системе и есть 16. Почему вычитание в двоичной системе работает именно так? Да нам это знать не обязательно. Для этого есть другие книги. Здесь же нам важно, что это просто работает.

Теперь рассмотрим умножение. В двоичной системе умножение еще проще, чем в десятичной. Здесь 1 х 1 = 1, а в остальных случаях, ответ – 0! Вот пример умножения 5 х 5 на бумаге в двоичном формате:

Посмотрим, что здесь происходит. Если правая цифра верхнего числа – 1, мы кладем нижнее число в ответ. Далее сдвигаем нижнее число влево. Если следующая цифра в верхнем числе 1, то к ответу прибавляем сдвинутое нижнее число, затем снова сдвигаем нижнее влево. И так, пока не сдвинем нижнее полностью. Короче, это проще понять, посмотрев программный код, где R0 – верхнее число, R1 – нижнее, R2 – ответ, а R3 используется для выхода из умножения:

Теперь посмотрим, что происходит с регистрами во время выполнения выполнения этой операции с петлей в первые три круга.

В силу того, что множители маленькие, на этом круге наше умножение уже выполнено: R1 был прибавлен к R2 дважды. Это случилось в первом круге, когда R1 = 0000 0101 (т.к. флаг С = 1) и на третьем круге, когда R1 был сдвинут влево уже дважды и R1 = 0001 0100 (флаг С = 1 снова). R2 теперь содержит 0001 1001, что составляет 16 + 8 + 1 или 25. Это является правильным ответом на выражение 5 х 5. Далее цикл повторится еще 5 раз, пока бит R3 не сдвинется к флагу переноса, но R2 больше не будет складываться с R1, так как в R0 больше нет единиц. Так эта программа выполняется за восемь проходов.

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

Деление двух чисел, например 15 / 3, можно описать как количество раз, когда из 15 можно вычесть 3, пока не останется 0.

Шаг 1: 15 – 3 = 12. Мы вычли один раз, поэтому в некий регистр (пусть это R2) запишем 1. Проверим флаг Z и если он не равен 0, то идем в начало.

Шаг 2: 12 – 3 = 9. R2 + 1 = 2. Z = 0? Идем в начало.

Шаг 3: 9 – 3 = 6. R2 + 1 = 3. Z = 0? Идем в начало.

Шаг 4: 6 – 3 = 3. R2 + 1 = 4. Z = 0? Идем в начало.

Шаг 5: 3 – 3 = 0. R2 + 1 = 5. Z = 0? Конец программы.

Существуют способы арифметических действий и с отрицательными числами, но здесь мы рассматривать это все не будем.

Работа с периферией

Мы уже знаем, как работает компьютер. Но вся его работа была бы напрасна, если бы результаты его вычислений просто оставались бы в регистрах ОЗУ. Как бы мы узнали, что получилось в конце выполнения программ?

Для этого и было придумано взаимодействие с некими периферийными устройствами, которые позволяют общаться между собой человеку и компьютеру. Человек вводит информацию в компьютер, а компьютер выводит информацию для человека. Это и есть «Ввод/вывод» или «i/o». Одним из примеров таких устройств является монитор. Все, что нам понадобится для организации ввода/вывода информации, это несколько проводов и новая инструкция.

Для начала, мы просто выведем шину ЦП за пределы компьютера и, за одно, еще четыре провода вместе с ней. Эти 12 проводов будут называться «Шина ввода/вывода» (i/o Bus). Устройства, подключенные к этой шине, называются «периферийными устройствами», т.к. расположены за пределами компьютера. К шине можно подключать несколько устройств, но работать с ними компьютер будет по очереди (только одно устройство единовременно). Поэтому, у каждого устройства должен быть свой уникальный адрес ввода/вывода. Это просто некое число, которое устройство распознает и понимает, что данные на шине предназначены именно ему. Вот так выглядит шина ввода/вывода ЦП:

На схеме ниже показаны провода шины ввода/вывода. CPU Bus – это та же шина данных ЦП.

Провод input/output – определяет, в какую сторону будут перемещаться данные по шине ЦП.

Data/Address – определяет, будем мы передавать данные или адрес.

I/O Address - определяет номер устройства, которое может быть подключено к шине ввода/вывода.

I/O clk e и I/O clk s – активируют и устанавливают регистры для перемещения данных.

Теперь нам кое-что нужно добавить в контрольную секцию:

Биты 4 и 5 регистра IR постоянно находятся на шине ввода/вывода. Но чтобы выполнить операцию ввода-вывода, нужно выполнить всего один шаг. Для вывода, во время 4 шага степпера, включается ‘e’ регистра Reg B, включается и выключается ‘s’ I/O clk s, а во время шагов 5 и 6 ничего не происходит. Для ввода, на 5 шаге включается ‘e’ I/O clk e и выставляется ‘s’ Reg B. На 4 и 6 шагах ничего не проичходит.

Теперь рассмотрим код инструкци ввода/вывода:

Эту инструкцию можно использовать четырьмя разными способами, в зависимости от битов 4 и 5 регистра IR. Поэтому в нашем машинном языке появляются четыре новых слова:

Каждое периферийное устройство имеет свои уникальные характеристики и нуждается в уникальном подключении к i/o шине. Этим «уникальным подключением» является «адаптер устройства» (device adapter). Каждый тип адаптера называеттся по-своему, например «адаптер клавиатуры» или «адаптер диска». Адаптер ничего не делает, пока на шине не появится его адрес. Как только это происходит, адаптер начнет реагировать на команды от компьютера. По инструкции «OUT Addr», компьютер включает провод «Address» и выставляет на шину имя устройства, с которым хочет общаться по шине ЦП. Периферийный адаптер узнает свой адрес и оживает. Любое другое устройство имеет другой адрес, поэтому не реагирует.

Углубляться в работу ввода/вывода информации, в схему с вентилями, мы не будем. В силу предыдущего опыта, вы и так понимаете, что никакой магии тут нет, а все переключения происходят с помощью все тех же простых элементов И и ИЛИ. Идея этой главы в том, что компьютер – это ЦП и ОЗУ. Остальные устройства, как клавиатура, жесткий диск, монитор, мышь, звуковая карта, интернет адаптер, - все это периферия, которая обменивается данными с компьютером. Адаптеры же обеспечивают согласование между компьютером и устройствами. Таким образом, компьютер управляет устройствами с помощью нескольких простых команд ввода/вывода.

Клавиатура

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

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

При нажатии клавиши, в регистре «Keycode» появляется ASCII код. Логический вентиль №1 имеет восемь входов. Как видно по схеме, этот вентиль сработает только в том случае, когда на шине ЦП будет код 0000 1111. Это адрес ввода/вывода этого адаптера клавиатуры. Элемент №2 сработает только при одновременном включении Clk s, Address и OUTинструкции. Этот вентиль включает вход ‘set’ бита памяти «М». Итак, когда шина ЦП содержит 0000 1111, вход ‘i’ будет включен и бит памяти так же включится. А когда бит памяти включен, это значит, что адаптер клавиатуры активен. Элемент №3 включается только в том случае, если активен Clk e, DATA и INинструкции. Таким образом, данные от клавиатуры будут переданы в процессор, если бит памяти и вентиль И №4 включены, а регистр Keycode будет подключен на шину, из которой данные будут установлены в регистр Reg B.

Каждый адаптер, подключенный к шине i/o, должен иметь некий свой тип схемы, включающий вентили №1, №2 и бит памяти, как в адаптере клавиатуры. Отличие будет в комбинации проводов включения вентиля №1, что позволит процессору выбирать нужное устройство.

Теперь рассмотрим пример программы записи нажатия клавиши в регистр Reg 3 в ЦП.

Программа проверяет состояние клавиш клавиатуры. Если ни одна кнопка не была нажата, то в R3 будет записано 0000 0000. Если какая-то клавиша была нажата, то в R3 запишется какой-то код ASCII и далее программа будет выполнять условие, соответствующее нажатию определенной клавиши. Так и работают устройства ввода/вывода и адаптеры.

Дисплей

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

Думаю, вы и без меня знаете, что движущаяся картинка на экране, на самом деле иллюзия, вызванная быстрой сменой статичных изображений.

Давайте, я быстро напомню вам, как работает экран телевизора. На экран выводится 30 кадров в секунду, т.е. один кадр отображается 1/30 секунды. Кадр на экране «рисуется» построчно слева направо и сверху вниз. Строки состоят из точек (пикселей). И единовременно, на самом деле, отображается только одна точка. Так слева направо точка за точкой рисуется строка. Когда отрисовка строки заканчивается, начинается отрисовка следующей строки. Между окончанием отрисовки предыдущей строки и началом отрисовки следующей есть некоторая пауза. Когда отрисована последняя нижняя строка, все начинается заново и рисуется следующий кадр. Между окончанием отрисовки предыдущего кадра и началом отрисовки следующего, существует некоторая пауза. И все это происходит на столько быстро, что мы не успеваем заметить отрисовкуу каждого пикселя и нам кажется, что на экране полная движущаяся картинка.

Дисплей это периферийное устройство. Именно дисплей занимается отрисовкой изображения на своем экране. А ЦП лишь сообщает, что нужно нарисовать на экране. Но если ЦП будет постоянно контролировать отрисовку каждого пикселя, то у него не останется времени на другую работу. Мы помним, что между отрисовками кадров есть некоторое время. Именно в это время наш компьютер и будет сообщать дисплею, что рисовать в следующем кадре. И чтобы запомнить кадр, дисплею понадобится собственная память.

Обратите внимание: США делают ставку на квантовые компьютеры.

Во время отрисовки кадра, ЦП может заниматься своими делами.

Давайте рассмотрим принцип работы дисплея на простом примере. Пусть наш подопытный дисплей будет устроен так, что каждый пиксель может принимать только одно состояние: включен или выключен. Размер экрана будет 320х200 пикселей. Всего получается 64000 пикселей и у каждого бйдет свой собственный адрес: у крайнего верхнего левого 0х0, а у крайнего правого нижнего 319х199. Еще нашему дисплею понадобится свой собственный тактовый генератор. Наш дисплей будет отображать 64000 пикселей 30 раз в секунду. Поэтому генератор должен работать с частотой 64000 х 30 = 1 920 000 Гц. Так как компьютер у нас 8-ми битный, то для хранения 64000 бит (один кадр) нам понадобится 8000 байт памяти.

В адаптере дисплея есть два регистра, которые указывают положение текущего пикселя по горизонтали и вертикали. Каждый такт адаптер дисплея прибавляет единицу к регистру горизонтального положения. Когда значение регистра достигает 319, следующий такт сбросит состояние горизонтального регистра до нуля и в этот момент к регистру вертикального положения прибавится единица. Когда значение вертикального регистра будет равно 199, адаптер сбросит его значение до нуля, при следующем такте. Таким образом за время отрисовки кадра, горизонтальный регистр 200 раз проходит значения от 0 до 319, а вертикальный регистр 1 раз проходит значения от 0 до 199. Таким образом, единовременно выбирается один пиксель экрана от 0х0 до 319х199. Затем все повторяется и рисуется следующий кадр.

Как вы помните, объем ОЗУ нашего дисплея 8 кБ, а в каждом байте записано состояние 8-ми пикселей. Поэтому адаптеру понадобится еще один регистр адреса ОЗУ, который будет увеличиваться на единицу, каждые восемь тактов. В итоге, это будет работать так: включается регистр адреса ОЗУ со значением 0000 0000 и устанавливается в MAR ОЗУ дисплея; Байт из ОЗУ считывается побитно и рисуются пиксели 0х0, 1х0, … , 7х0; к регистру адреса ОЗУ дисплея прибавляется единица и уже следующий байт из ОЗУ побитно выводится на экран в пиксели 8х0, … , 15х0; и т.д. до момента, когда регистр адреса ОЗУ станет равен 1111 1111 и отрисуются пиксели 313х199, … , 319х199. Со следующим тактом все начнется сначала и регистр адреса ОЗУ сбросится на ноль.

Как уже говорилось ранее, большую часть времени адаптер дисплея тратит на отрисовку экрана. Единственное, что нужно сделать между отрисовками кадров, это принять команды с шины ввода/вывода, которые изменят содержимое ОЗУ видеоадаптера. Чтобы обновить данные в ОЗУ дисплея, программа использует команду I/O OUT, чтобы выбрать адаптер дисплея, а затем по адресам ОЗУ дисплея отправляются нужные байты. Стоит обратить внимание, что ОЗУ дисплея устроена немного иначе, чем ОЗУ ЦП. Здесь у ОЗУ есть два разделенных регистра MAR. Суть в том, что для ввода данных используется одна сетка MAR, а для вывода – другая.

Так и работает дисплей. Но что, если мы хотим вывести на экран какой-нибудь символ, например «А» ASCII? Если мы просто запишем код символа «А» в байт ОЗУ дисплея по адресу 0х0, то дисплей отрисует на экране первые восемь пикселей 01000001. Но это ведь не то, что мы хотели, не правда ли? Значит, записывать информацию в ОЗУ дисплея надо как-то иначе. Далее мы рассмотрим, как это делать.

Другой код

Из прошлой главы мы уже поняли, что нельзя вот так просто взять и записать символы ASCII в ОЗУ дисплея. Теперь нам надо придумать способ, который позволит нам преобразовывать символы кодировки ASCII в читаемое изображение букв. Давайте определимся с размером наших изображаемых букв. Пусть это будет 8х8 пикселей. Таким образом, для отображения одного символа на экране нам понадобится 8 байт. К примеру, символ «Е» (0100 0101) на экране будет отображаться вот так:

Восемь байт – это не так много. Но что, если мы захотим отобразить текст из 100 символов? Тогда нам понадобится 800 байт, но в ОЗУ нашего компьютера есть только 256 байт! Дело в том, что мы проектировали простейший компьютер с 8-ми битной адресной шиной. Но на самом деле, бывают более сложные 8-ми битные процессоры с 16-ти битной адресной шиной, которые могут адресовать до 65536 байт. Поэтому, сейчас просто представим, что размер ОЗУ может быть больше.

Эти 800 байт представляют из себя тип кода, известный как «Шрифт». Чтобы отобразить на экране какой-либо символ в определенном месте, нужно будет просто выбрать нужную картинку из шрифта и потом использовать инструкции ввода/вывода для копирования восьми байт в нужные регистры ОЗУ дисплея.

Если картинки в нашем шрифте расположены по очереди, то для поиска нужной, нам нужно будет просто умножить код символа ASCII на восемь. И полученное число будет адресом первого из восьми байт, в которые записана соответствующая картинка. Например код символа «Е» - 0100 01011, что в десятичной системе равно 69. Так «Е» - это 69 для компьютера. Теперь 69 умножаем на 8 и получаем 552. Соответственно в адресах памяти компьютера с 552 по 558 расположена нужная нам картинка.

Теперь нам нужно правильно переписать эти 8 байт в ОЗУ дисплея. Допустим, мы хотим нарисовать букву «Е» в верхнем левом углу экрана. Первые восемь пикселей первой строки – это первый байт ОЗУ дисплея, находящийся по адресу 0. Здесь мы с помощью инструкций ввода/вывода скопируем данные из 552 адреса ОЗУ в 0 адрес ОЗУ дисплея. Теперь нам нужна вторая строка в ОЗУ дисплея. Где она расположена? В каждой строке дисплея 320 пикселей, что соответствует 320 / 8 = 40 байт. Так первая строка занимает с 0 по 39 байт ОЗУ дисплея. Значит второй байт картинки «Е» из 553 адреса ОЗУ нужно записать в 0 + 40 = 40 адрес ОЗУ дисплея. Так же с третьего по восьмой байты картинки мы запишем в 80, 120, 160, 200, 240 и 280 адреса ОЗУ дисплея. После записи всех восьми байтов, вы увидите на экране изображение в виде буквы «Е».

Если вы захотите отобразить рядом с буквой «Е» букву «Х», то байты ее картинки нужно будет записать в 1, 41, 81, 121, 161, 201, 241, 281 адреса ОЗУ дисплея. Если вам нужно будет написать одну и ту же букву 10 раз, то просто скопируйте ее картинку в 10 разных мест в ОЗУ дисплея.

Конечно, это все выглядит как огромная работа для вывода всего одной буквы на экран. Программа делает много циклов, в которых определяет адреса картинки символа, затем выводит байт в ОЗУ дисплея. Мы не будем здесь писать эту программу. Могу лишь сказать, что нам потребуется около 50 инструкций, которые будут выполняться примерно восемь раз, чтобы вывести одну букву на экран. Это значит, что на отрисовку 1000 символов на экране, понадобится около 400 000 командных циклов. С одной стороны, это довольно долго. Но с другой, это лишь четверть процента того, что может сделать такой процессор за одну секунду. Поэтому-то компьютеры и должны так быстро работать.

Жесткий диск

Все знают, что жесткий диск – это устройство для хранения данных. Это на самом деле так. Диск умеет только принимать и передавать данные на шину ввода/вывода и ничего больше.

Диски используются по двум причинам. Первая – на них можно хранить много данных. Вторая – они не «забывают» информацию при отключении питания, в отличие от ОЗУ.

Жесткий диск так называется потому, что внутри в самом деле расположен металлический диск с магнитными кольцевыми дорожками. Именно на этих дорожках и хранится информация. Дорожки эти разделены на несколько участков, называемых секторами. Запись и чтение с диска производится с помощью магнитной головки, закрепленной на подвижном рычаге. К слову, дисков и головок может быть несколько в одном таком устройстве. Организация адресов в жестком диске немного отличается от ОЗУ. Здесь данные хранятся блоками (кластерами) в несколько тысяч байт. И прочитать отдельный байт блока мы не можем. Можно лишь скопировать целый кластер в ОЗУ. Когда ЦП обращается к ОЗУ, нужно указать адрес байта. В случае с жестким диском, нужно указать номер головки (номер блина с информацией), номер дорожки и номер сектора. Теперь можно прочитать или записать целый кластер.

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

Прерывание

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

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

Прерывание начинается с добавления еще одного провода к I/Oшине. Этот провод используется некоторыми устройствами, чтобы сообщить процессору, что пора выполнить операцию ввода/вывода, так как была нажата клавиша на клавиатуре. После сигнала «прерывание», при следующем 1 шаге контрольной секции будет выполняться не следующая операция, а определенные действия:

В результате этой операции, текущие значения IAR и Flags запишется в адреса 0 и 1 ОЗУ, а затем заменятся данными из адресов 2 и 3 ОЗУ соответственно. Затем процессор продолжит работу в обычном режиме. Но мы заменили IAR и теперь процессор начнет выполнение инструкции, находящейся по адресу из байта 2 ОЗУ. Другими словами, то что процессор делал, сохраняется и ЦП выполняет какую-то другую задачу. В конце этого действия, программа из байтов 0 и 1 ОЗУ возвращает данные на место и ЦП продолжит выполнение старого задания с того места, где был прерван. Прерывание очень полезно при работе с операциями ввода/вывода. Иначе, процессору будет необходимо регулярно опрашивать через I/O шину каждое периферийное устройство. Но с системой прерываний ЦП может выполнять основную программу сколько угодно, не беспокоясь о том, что пропустит ввод с клавиатуры, например.

Мы не включили систему прерываний в наш ЦП, так как схема контрольной секции стала бы слишком сложной и большой. Нам пришлось бы добавить следующие вещи: еще два шага степпера, провода и вентили для выполнения 8-ми команд прерывания, подключение регистра флагов к шине данных, способы отправки двоичного числа 0, 1, 2 и 3 в MAR, инструкцию восстановления IAR и Flags из байтов 0 и 1 ОЗУ. Поэтому ограничимся теоретическим объяснением.

Вот и всё, ребята!

На этом можно и закончить описание основных принципов работы компьютера. Несомненно, есть еще масса нюансов в устройстве этой самой "волшебной коробки". Бывают и другие инструкции процессора, а сами процессоры могут быть не только 8-ми битными, но и 16-ти, и 32-х, и 64-х, и даже 128-ми битными! Есть и бесконечное множество устройств периферии, со своими принципами работы и способами общения с шиной i/o. А что касается программирования и его уровней, так тут вообще просто бесконечное поле для обучения. Задачей этой серии статей было знакомство с внутренним устройством и основными принципами работы ЦП и ОЗУ, в первую очередь.

Теперь же, будь вы начинающий радиолюбитель, или программист, или просто любопытный человек, компьютер для вас это никакая не "волшебная машина", а вполне понятный механизм, работающий вполне понятным образом. Надеюсь, вам было интересно узнавать о том, как устроен процессор и как он взаимодействует с остальными компонентами компьютера. Спасибо вам за внимание и потраченное на прочтение этих статей время. Удачи вам в учебе и во всех начинаниях!

Часть 9 - Назад

#компьютеры #наука и техника #образование #начинающим радиолюбителям #электроника начинающим #процессоры #программирование

Еще по теме здесь: Новости науки и техники.

Источник: Как устроен компьютер? Часть 10 (Как работат периферия).