Sega Master System 02: Внутреннее устройство, процессор

Сегодня мы будем разбираться как работает SMS и из каких основных компонентов она состоит. В первую очередь нам необходимо понять как работает самый главный компонент - центральный процессор, потому что именно вокруг него построена вся система. Сразу хочу заметить что этот пост и следующий будут содержать много теории. Несмотря на то что я буду пытаться излагать всё максимально просто и вводить термины постепенно. Эти теоретические посты можно использовать в виде справочной информации. Но первое обзорное прочтение - крайне рекомендуется.

Перед началом ещё стоит упомянуть что я ожидаю от вас хотя бы какого-то понимания того что такое система счисления, зачем нужна двоичная и шестнадцатиричная системы счисления, Умеете производить простейшие операции над числами в шестнадцатиричном представлении хотя бы при помощи калькулятора. И знаете что такое бит и байт. Обозначения систем счисления будем использовать из ассемблера с которым будем дальше работать. Десятичные числа будут обозначены как обычно: 1, 10, 42. Шестнадцетеричные будут начинаться со знака доллара $01, $0A, $2A, а также шестнадцатиричные числа будут по необходимости дополнены ведущими нулями когда нужно указать размер числа в байтах. $12, $02 - однобайтовые, $113F, $002A - двухбайтовые. Двоичные числа будут начинаться со знака процента, например %00101010, добавление ведущих нулей по тем же правилам как в шестнадцатеричной системе. Помимо указанных форматов записи чисел существуют и другие популярные, такие как 0x2A, 2Ah для шестнадцатеричных и 00101010b, B00101010. Но использовать мы их не будем, чтобы не вводить лишние сущности и путаницу.

Процессор.

Мозгом SMS является процессор NEC 780C являющийся клоном популярного в то время Zilog Z80, так что для простоты мы можем провести между ними знак равенства. Далее для краткости я буду называть его просто z80. Для того чтобы программировать под z80 на ассемблере нам необходимо понимать логику его работы и внутренне устройство, не пугайтесь, мы не будем уходить в кремниево-транзисторные дебри. Нам достаточно внутреннего логического устройства. Хотя часть его ножек мы всё же посчитаем =)

Основная задача процессора - выполнять поступающие к нему команды. Процессор берёт команды одну за другой из внешней памяти. Каждая команда это просто последовательность из нескольких чисел в диапазоне от 0 до 255 ($00 - $FF). В дальнейшем такое число мы будем называть просто байтом, потому что как вы знаете - байт состоит из 8 бит что несёт информацию о чём-то одном из 256 вариантов. Но можно не особо заморачиваться и просто число в диапазоне 0-255 ($00-$FF) называть байтом. Итак, каждая команда для процессора состоит из одного или нескольких байт, а последовательность таких команд называется программой.

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

Память.

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

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

Для общения с памятью на процессоре есть 24 ножки. 16 из них предназначены для отправки адреса, а оставшиеся 8 для получения или отправки самого числа. При помощи первой группы ножек мы можем представить 16битное число в диапазоне от 0 до 65535 (от $0000 до $FFFF) которое будет являться адресом ячейки, а при помощи второй группы из 8 ножек мы можем читать или отправлять 8-битное число. Такие группы ножек называются шинами. И чтобы обозначить для чего эти шины говорят Адресная шина и Шина данных соответственно. А количество ножек задействованных в шине называют шириной шины. Каждую ножку и подходящий к ней проводник принято называть линией шины. Для простоты запоминания можете рассматривать линии шины как провода соединяющие разные компоненты, а сами шины как пучки проводов, Получается что мы имеем 16-битную адресную шину и 8-битную шину данных.

Стоит сразу объяснить почему адресная шина имеет ширину 16 бит. Если бы ширина адресной шины была 8 бит, то мы могли бы по ней передать только 8-битное или однобайтовое число, а это всего лишь число от 0 до 255 ($00 - $FF). То есть процессор мог бы работать всего лишь с 256 ячейками памяти в которых хранятся числа размером в байт каждое. Этого очень мало для игр которые могут раскрыть потенциал SMS. А вот 16-битная шина уже позволяет процессору работать с 65536 ячейками памяти, что уже не так плохо. Но почему размер адресной шины был выбран именно такой - 16 бит? Ответ прост, 16 бит это - ровно 2 байта. А когда байт целое количество, то их гораздо удобнее хранить в памяти да и работать с ними тоже.

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

И последнее важное уточнение про память. Весь диапазон ячеек памяти адрес которых может выразить процессор (65536 ячеек) называется адресным пространством. Адресное пространство это вообще вся память к которой может обращаться процессор, а не содержимое какой-то одной специальной микросхемы. Процессору вообще всё равно сколько микросхем да и что вообще творится на другом конце шин адреса и данных, важно только что на другом конце шины данных "играют" по тем же правилам, что и процессор. Благодаря тому что адресная шина представляет собой 16 отдельных линий, то путём нехитрых схемотехнических решений в адресное пространство можно подключить несколько разных типов памяти из разных микросхем. Например в диапазоне адресов $0000-$3FFF будет доступна системная память отвечающая за загрузку и выполнение первичных действий и эта память будет неизменяемой, а в диапазоне $4000-$4FFF - оперативная память использующаяся для хранения изменяемых данных. Эти диапазоны просто пример. Реальное распределение памяти в SMS мы будем рассматривать позже.

Внутреннее устройство процессора.

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

  • Устройство управления
  • Регистры
  • АЛУ - арифметико-логическое устройство
  • Дешифратор команд

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

Регистрами процессора называются внутренние ячейки памяти процессора. Да в процессоре есть своя память. Но её очень мало и у каждой ячейки есть своё назначение. В них содержатся Данные необходимые для выполнения какой-то одной операции либо результат уже выполненной операции. Чуть позже мы подробно разберём тему регистров.

АЛУ - это по сути калькулятор. Этот компонент процессора позволяет проводить над двумя числами такие операции как сложение и простые логические операции "И", "ИЛИ" и исключающее "ИЛИ", а так же битовые сдвиги.

Дешифратор команд - это очень интересный компонент. Он анализирует каждый входящий байт программы и определяет что с ним дальше делать процессору. Проектировщиками в процессор заложен определённый список элементарных действий которые он может выполнять, эти действия называются командами. Каждой команде присвоен номер и сценарий выполнения. Дешифратор перед выполнением новой команды процессором читает поступивший к нему байт и в зависимости от его значения либо сразу переходит к выполнению команды, либо читает ещё несколько байт. Как я упоминал ранее команды могут быть разной длины. Например если пришло число $78, то дешифратор знает что за этим числом скрывается операция которая описывается одним байтом, в данном случае это операция копирования значения из регистра B в регистр A. После чего процессор приступит к чтению следующей команды. И если следующий байт окажется $3A, то дешифратор даст команду прочитать ещё два байта из программы, потому что с байта $3A начинается трёхбайтовая команда копирования значения из ячейки внешней памяти в регистр A. О том почему команды бывают разной длины и что значат все их байты мы будем изучать на конкретных командах по мере их первого появления и использования.

Регистры.

Давайте подробнее разберём регистры. Как я уже писал, регистры - это внутренние ячейки памяти которые находятся непосредственно в процессоре и нужны для его работы. Их совсем немного в сравнении с внешней памятью и обозначаются они не номерами (адресами), а буквами. A, F, B, C, D, E, H, L, I, R, IX, IY, SP, PC. Все они нужны для разных целей. Во-первых, они различаются по размеру. Есть однобайтовые регистры, а есть двухбайтовые, соответственно в однобайтовых могут содержаться числа от 0 до 255 ($00 - $FF), в двухбайтовых от 0 до 65535 ($0000 - $FFFF). Размер регистров легко определить по их обозначению. Однобайтовые обозначаются одной буквой. Двухбайтовые - двумя.

Помимо своих размеров регистры делятся по назначению.

  • Регистры общего назначения - A, B, C, D, E, H, L
  • Регистры специального назначения - PC, I, R, SP, IX, IY, F

Регистры общего назначения.

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

У регистров общего назначения есть одна интересная особенность. Они могут объединяться в регистровые пары - AF, BC, DE, HL. Как это работает. Вот есть у нас регистр B в котором находится число $17, а в регистре C находится число $A7, и если мы обратимся к регистру BC то получим число $17A7, то есть мы можем с двумя однобайтовыми регистрами работать как с одним двухбайтовым, например для работы с адресом.

Среди регистров общего назначения выделяются регистры A и HL. Регистр A называется аккумулятором. Это название пришло из истории "процессоростроения". Многие более старые процессоры имели всего один регистр который назывался аккумулятором, в него складывались результаты вычислений и с его содержимым совершались все основные действия процессора. В z80 эта традиция сохранилась. Многие команды работают только с регистром A, например команда сложения прибавляет к содержимому регистра A второе число из команды сложения. Примерно такая же история и с HL, отличие лишь в том что многие операции для работы с 16битными числами работают только с регистром HL.

Регистры специального назначения всегда выполняют свою особую функцию вне зависимости от того какая команда процессора сейчас выполняется. Можно сказать что это своего рода "настройки" процессора или его индикаторы.

PC - программный счетчик или счётчик комманд.

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

Есть специальные инструкции перехода, они записывают в этот регистр указанный в этой команде адрес. Но это подробнее мы разберём на практике. Когда будем осваивать переходы и ветвления.

I - Вектор прерываний

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

R - Регистр регенерации памяти.

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

SP - Регистр стека

Этот регистр содержит в себе указатель на вершину стека. Стек это особая область в памяти предназначенная для хранения адресов возврата или наших промежуточных данных. Подробнее Стек и его работу мы разберём далее на практике.

XI и YI - Индексные регистры

Это два 16-битных регистра. Они предназначены для хранения адресов. Например для адреса каких-то важных данных к которым часто идёт обращение или адрес начала целой секции данных. Такой адрес будет использоваться как базовое значение. Например когда нам нужно получить 8-й байт после адреса хранящегося в XI, мы просто прибавим к нему 8 и получим адрес нужного нам байта.

F - Регистр флагов

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

Ниже список назначения битов этого регистра:

  • бит 0 - С - флаг переноса. Приобретает значение 1 когда происходит перенос или заём в арифметических операциях, другими словами когда регистр A переполняется и его значение становится больше 255 или меньше 0
  • бит 1 - N - флаг отрицательного результата, Приобретает значение 1 когда в ходе арифметической операции был получен отрицательный результат
  • бит 2 - P/V флаг переполнения/четности, приобретает значение 1 когда в логических операциях получается чётный результат, а в арифметических при переполнении результата.
  • бит 3 - этот бит не используется
  • бит 4 - H флаг вспомогательного переноса. Разберём его позже на практике, если это понадобится.
  • бит 5 - этот бит не используется
  • бит 6 - Z флаг нуля. Приобретает значение 1 когда результат операции равен нулю. Либо при операции сравнения получен истинный результат.
  • бит 7 - S флаг знака. Приобретает значение 1 если последняя арифметическая операция была вычитанием.

Альтернативный набор регистров. A', B', C', D', E', H', L', F'

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

Порты.

Порты - это средство общения процессора с другими компонентами системы, кроме памяти. Но для начала вернёмся к описанию того как процессор работает с памятью и к описанию шин: адресной, данных и управления. Самые внимательные читатели могли заметить что в описании шины данных я упомянул, что она может использоваться как для чтения данных из памяти так и для записи в память. Но как процессору и памяти договориться о том когда процессор хочет прочитать что-то из памяти, а когда что-то записать туда? Здесь нам на помощь приходит шина управления. Она несколько отличается от адресной шины и шины данных, если в адресной шине и шине данных все линии выполняют одну и ту же функцию - являются представлением определённого разряда передаваемого числа в двоичной системе счисления. То в шине управления каждая линия имеет свою отдельную функцию.

Для того уведомить устройства подключенные к шине данных о том что именно собирается делать процессор на этой шине, читать или писать в неё. В шине управления существуют линии RD и WR сигнализирующие о намерениях процессора, по линии RD (read) передаётся сигнал о том что процессор намерен прочитать данные, а по линии WR (write) - что он собирается передавать данные.

Теперь вернёмся к портам. Механизм процессора для работы с памятью через две шины оказался настолько удобным и универсальным, что его решили использовать и для общения с другими устройствами, но с небольшими модификациями. Все периферийные устройства подключаются к этим же двум шинам что и память. Каждое периферийное устройство становится для процессора похожим на обычную ячейку памяти или несколько ячеек. Каждое устройство реагирует на свой номер или набор номеров выставленных на шине адреса. И такая "ячейка" называется портом. Для простоты понимания можете представить себе что память это большой шкаф с ящиками, ячейки памяти это ящики шкафа в которых лежат данные, а порты это соседний шкаф, с точно таким же количеством ящиков с той лишь разницей что у каждого из ящиков вместо задней стенки есть ещё одна дверца, а с другой стороны находится периферийное устройство и через вот такой ящичек с двумя дверцами процессор и устройство обмениваются данными.

Осталось дело за малым - понять когда процессор пытается общаться с памятью, а когда с портами. Для этого в шине управления существуют ещё две специальные линии MREQ и IOREQ. Через линию MREQ (memory request) процессор даёт системе понять, что он собирается работать с памятью, а через линию IOREQ (input/output request) - что он собирается работать с портами.

Внутреннее устройство консоли

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

Image

Знаком вопроса отмечены компоненты присутствующие не во всех версиях SMS.

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

В следующем посте мы разберём как работает самый сложный и важный, после центрального процессора, компонент консоли - видеопроцессор. А так же разберём как работает графика в SMS и похожих системах в целом.