кр гаряев. Чарльз Петцольд - Код_ тайный язык информатики-Манн, Иванов и Фе. Книга принадлежит Контакты владельца Культовая книга талантливого преподавателя стала для многих первым уверенным шагом в программировании
Скачать 6.11 Mb.
|
Код Команда Код Команда C5 PUSH BC C1 POP BC D5 PUSH DE D1 POP DE E5 PUSH HL E1 POP HL F5 PUSH PSW F1 POP PSW Глава 19. Два классических микропроцессора 311 Команда PUSH BC сохраняет в стеке значения регистров B и C, а команда POP BC извлекает их. Аббревиатура PSW в последней строке означает слова состояния программы, которые, как вы помните, представляют собой 8-битный регистр, содер- жащий флаги. Две команды в нижней строке фактически помещают и извлекают из стека содержимое как аккумулятора, так и регистра PSW. Если вы хотите сохранить содержимое всех регистров и значения всех флагов, используйте следующие команды. PUSH PSW PUSH BC PUSH DE PUSH HL Когда вам потребуется восстановить содержимое этих регистров, обра- щайтесь к командам POP в обратном порядке. POP HL POP DE POP BC POP PSW Как работает стек? Предположим, что указатель стека равен 8000h. При выполнении команды PUSH BC происходит следующее: ȣ значение указателя стека уменьшается на 1 и становится равным 7FFFh; ȣ содержимое регистра B сохраняется по адресу, соответствующему значе- нию указателя стека, то есть в ячейке 7FFFh; ȣ значение указателя стека уменьшается на 1 и становится равным 7FFEh; ȣ содержимое регистра C сохраняется по адресу, соответствующему значе- нию указателя стека, то есть в ячейке 7FFEh. Команда POP BC, выполняемая при значении указателя стека, все еще равном 7FFEh, производит обратные операции: ȣ содержимое регистра C загружается из ячейки, адрес которой соответ- ствует значению указателя стека, то есть из ячейки 7FFEh; ȣ значение указателя стека увеличивается на 1 и становится равным 7FFFh; ȣ содержимое регистра B загружается из ячейки, адрес которой соответ- ствует значению указателя стека, то есть из ячейки 7FFFh; ȣ значение указателя стека увеличивается на 1 и становится равным 8000h. 312 Код Каждая команда PUSH увеличивает размер стека на два байта. Существу- ет вероятность того, что из-за ошибки в программе размер стека станет на- столько большим, что его содержимое начнет сохраняться в ячейках, занятых необходимым программе кодом или данными. Эта проблема называется пе- реполнением стека. Точно так же слишком большое количество команд POP может привести к преждевременному исчерпанию стека. Если к процессору 8080 подключена память объемом 64 килобайт, имеет смысл установить начальное значение указателя стека равным 0000h. Первая команда PUSH уменьшает это значение на 1 — до FFFFh. После этого стек зай- мет область памяти с самыми высокими адресами, которая максимально уда- лена от ваших программ, хранящихся, вероятно, начиная с 0000h. Установить значение указателя стека можно с помощью команды LXI (Load Extended Immediate — расширенная непосредственная загрузка). Перечислен- ные далее команды также загружают в 16-битные пары регистров два байта, которые следуют за кодом команды. Код Команда 01 LXI BC, xxxx 11 LXI DE, xxxx 21 LXI HL, xxxx 31 LXI SP, xxxx Команда LXI BC,527Ah эквивалентна следующим командам. MVI B,52 MVI C,7Ah Однако команда LXI позволяет сэкономить один байт. Кроме того, по- следняя команда LXI в предыдущей таблице используется для установки кон- кретного значения для указателя стека. Часто эта команда одной из первых выполняется микропроцессором после его перезапуска. 0000h: LXI SP,0000h Увеличить и уменьшить на 1 значение пары регистров и указателя стека можно с помощью следующих команд. Глава 19. Два классических микропроцессора 313 Код Команда Код Команда 03 INX BC 0B DCX BC 13 INX DE 1B DCX DE 23 INX HL 2B DCX HL 33 INX SP 3B DCX SP Рассмотрим еще несколько 16-битных команд. Следующие команды скла- дывают содержимое 16-битных пар регистров с содержимым пары регистров HL. Код Команда 09 DAD HL, BC 19 DAD HL, DE 29 DAD HL, HL 39 DAD HL, SP Эти команды позволяют сэкономить несколько байтов. Например, первая из них, как правило, требует шесть байт. MOV A, L ADD A, C MOV L, A MOV A, H ADC A, B MOV H, A Команда DAD обычно используется для вычисления адресов ячеек памя- ти и влияет только на флаг переноса. Следующие два кода команд сопровождаются 2-байтовым адресом ячей- ки памяти и позволяют сохранить содержимое пары регистров HL в соответ- ствующей ячейке, а также загрузить из нее содержимое в пару регистров HL. Код Команда Значение 2h SHLD [aaaa], HL Записать число из HL в PC 2Ah LHLD HL,[aaaa] Загрузить данные в HL Содержимое регистра L сохраняется по адресу aaaa, а содержимое регист - ра H — по адресу aaaa + 1. 314 Код Эти две команды загружают в счетчик команд (PC) или в указатель стека (SP) значение из пары регистров HL. Код Команда Значение E9h PCHL PC, HL Загрузить значение HL в PC F9h SPHL SP, HL Загрузить значение HL в SP Команда PCHL — своеобразная команда перехода. После нее процессор 8080 выполняет команду, код которой занимает ячейку по адресу, записанно- му в паре регистров HL. Команда SPHL — еще один способ установки значе- ния указателя стека. Следующие две команды позволяют поменять местами содержимое реги- стров HL с двумя байтами, являющимися «верхними» элементами стека, или с содержимым пары регистров DE. Код Команда Значение E3h XTHL HL,[SP] Поменять местами «верхний» элемент стека и HL EBh XCHG HL, DE Поменять местами DE и HL Из всех команд перехода для процессора 8080 пока я описал только PCHL. Как вы помните из главы 17, процессор предусматривает регистр под названи- ем «счетчик команд», содержащий адрес ячейки памяти, из которой процессор извлекает следующую команду, подлежащую выполнению. Как правило, счет- чик команд заставляет процессор выполнять команды, сохраненные в памяти, последовательно. Однако так называемые команды перехода, или ветвления, позволяют процессору отклониться от этого главного курса. Эти команды за- гружают в счетчик команд другое значение, поэтому следующая команда из- влекается процессором из какой-то другой области памяти. Несмотря на удобство обычной команды перехода, команды условного пе- рехода более удобные, поскольку заставляют процессор переходить к другому адресу, основываясь на значении определенного флага, например флага перено- са или флага нуля. Именно реализация условного перехода превратила автома- тизированный сумматор из главы 17 в универсальный цифровой компьютер. В процессоре 8080 имеется пять флагов, четыре из которых используют- ся для реализации условных переходов. Набор команд 8080 содержит девять команд безусловных и условных переходов, зависящих от того, чему равны флаги нуля, переноса, четности и знака: 1 или 0. Глава 19. Два классических микропроцессора 315 Прежде чем продемонстрировать эти команды, хочу познакомить вас с дву- мя другими типами команд, имеющих отношение к переходу. Первая — CALL (вызов), она аналогична команде перехода, за исключением того, что перед загрузкой нового адреса в счетчик команд процессор сохраняет предыдущий адрес. Где он сохраняет этот адрес? Разумеется, в стеке! Эта стратегия подразумевает, что команда вызова сохраняет информацию о том месте, откуда был совершен переход. Сохраненный адрес позволяет про- цессору вернуться в исходное местоположение. Команда для совершения об- ратного перехода называется RET (Return — вернуться). Она удаляет из стека 2-байтное значение и загружает его в счетчик команд. Команды CALL и RET — чрезвычайно важные функции любого процес- сора, позволяющие программисту реализовывать подпрограммы, которые яв- ляются часто используемыми фрагментами кода. (Под словом «часто» обычно я имею в виду «более одного раза».) Подпрограммы — основные организую- щие элементы программ на языке ассемблера. Обратимся к примеру. В процессе написания программы на языке ассемблера у вас возникает необходимость в перемножении двух байтов. Вы пишете код для выполнения этой операции, а затем продолжаете работу с программой. На каком-то этапе вам снова требуется перемножить два байта. Поскольку вы уже знаете, как это сделать, можно просто использовать те же команды снова и снова. Собирае- тесь ли вы во второй раз ввести эти команды в память? Надеюсь, что нет, посколь- ку это пустая трата времени и памяти. Вместо этого вам следует просто перейти к предыдущему фрагменту кода. Правда, в данном случае обычная команда пере- хода не сработает, поскольку она не позволяет вернуться к тому месту, с которого был совершен переход. Именно в этом случае пригодятся команды CALL и RET. Набор команд, позволяющих перемножить два байта, идеально подходит на роль подпрограммы. Давайте рассмотрим одну из них. В главе 17 подлежа- щие перемножению байты и произведение хранились в определенных ячейках памяти. Приведенная далее подпрограмма 8080 умножает байт в регистре B на байт в регистре C и помещает 16-битное произведение в регистр HL. Multiply: PUSH PSW ; Сохранение изменяемых регистров PUSH BC SUB H, H ; Установить HL (результат) SUB L, L ; в 0000h MOV A, B ; Сохранение множителя в А CPI A,00h ; Если он равен 0, 316 Код JZ AllDone ; завершить программу MVI B,00h ; Сохранение значения 0 в старшем байте BC MultLoop: DAD HL, BC ; Сложение значений HL и BC DEC A ; Уменьшение множителя на 1 JNZ MultLoop ; Возврат к началу цикла,если не 0 AllDone: POP BC ; Восстановление значений POP PSW ; регистров RET ; Возврат Обратите внимание: первая строка подпрограммы начинается с метки Multiply. Эта метка соответствует адресу ячейки памяти, в которой располо- жена подпрограмма. Подпрограмма начинается с двух команд PUSH. Как пра- вило, она пытается сохранить (а в дальнейшем восстановить) значения любых регистров, которые могут ей потребоваться. Затем подпрограмма записывает значение 0 в регистры H и L. Для это- го вместо команды SUB можно было бы использовать команду MVI (Move Immediate — переместить непосредственно), однако в этом случае потребова- лись бы четыре команды, а не две. После выполнения подпрограммы в паре регистров HL будет содержаться произведение. После этого подпрограмма перемещает содержимое регистра B (мно- житель) в A и проверяет, не равно ли оно 0. Если оно равно 0, подпрограм- ма завершается, так как произведение — 0. Поскольку значения в регистрах H и L уже равны 0, подпрограмма может просто использовать команду JZ (Jump If Zero — перейти, если ноль), чтобы перейти к двум командам POP в конце программы. В противном случае подпрограмма записывает в регистр B значение 0. Теперь в паре регистров BC содержится 16-битное множимое, а в аккуму- ляторе (А) — множитель. Команда DAD прибавляет значение BC (множи- мое) к значению HL (произведение). Значение множителя в A уменьшается на 1. Пока он не станет равен 0, выполнение команды JNZ (Jump If Not Zero — перейти, если не ноль) будет приводить к повторному сложению значения BC со значением HL. Этот небольшой цикл будет выполняться до тех пор, пока количество операций сложения BC и HL не станет равным множителю. (Более эффективную подпрограмму для умножения можно написать, исполь- зуя команды сдвига из набора команд процессора 8080.) Эту подпрограмму для перемножения чисел, например 25h и 12h, можно использовать в программе, добавив следующий фрагмент кода. Глава 19. Два классических микропроцессора 317 MVI B,25h MVI C,12h CALL Multiply Команда CALL сохраняет в стеке значение счетчика команд, которое пред- ставляет адрес команды, следующей после CALL. Затем CALL вызывает пере- ход к команде, на которую указывает метка Multiply. Это начало подпрограммы. После того как подпрограмма рассчитает произведение, она выполнит команду RET, в результате чего в счетчик команд будет возвращено значение из стека. Затем будет выполнена команда, следующая после CALL. Набор команд процессора 8080 предусматривает условные команды вы- зова и возврата,однако они используются реже, чем обычные команды пере- хода. Все они перечислены в следующей таблице. Условие Код Команда Код Команда Код Команда Нет C9 RET C3 JMP aaaa CD CALL aaaa Не ноль (флаг нуля не установ- лен) C0 RNZ C2 JNZ aaaa C4 CNZ aaaa Ноль (флаг нуля установлен) C8 RZ CA JZ aaaa CC CZ aaaa Нет переноса (флаг переноса не установлен) D0 RNC D2 JNC aaaa D4 CNC aaaa Есть перенос (флаг переноса уста- новлен) D8 RC DA JC aaaa DC CC aaaa Результат нечетный (флаг четности не уста- новлен) E0 RPO E2 JPO aaaa E4 CPO aaaa Результат четный (флаг четности установлен) E8 RPE EA JPE aaaa EC CPE aaaa Результат положи- тельный (флаг знака не установлен) F0 RP F2 JP aaaa F4 CP aaaa Результат отрицатель- ный (флаг знака уста- новлен) F8 RM FA JM aaaa FC CM aaaa 318 Код Как вы знаете, к микропроцессору подключается не только память. Ком- пьютерная система обычно требует устройства ввода и вывода (I/O), которые облегчают пользователям взаимодействие с машиной. К этим устройствам, как правило, относятся клавиатура и дисплей. Как микропроцессор взаимодействует с этими периферийными устрой- ствами? (Все подключенные к микропроцессору компоненты, кроме памяти, называются периферийными.) Конструкция периферийных устройств, подоб- но памяти, предусматривает интерфейс. Микропроцессор может записывать и считывать данные с периферийного устройства, указывая определенные адреса, на которые оно реагирует. В некоторых микропроцессорах перифе- рийные устройства фактически задействуют адреса, обычно используемые для обращения к памяти. Такая конфигурация называется вводом-выводом с распределением памяти. Тем не менее в процессоре 8080, кроме обычных 65 536 адресов для устройств ввода и вывода, специально зарезервированы 256 дополнительных, которые называются портами ввода/вывода. Адрес- ные сигналы устройств ввода/вывода подаются на входы с A 0 по A 7 , а от об- ращений к памяти их отличают сигналы, фиксируемые чипом системного контроллера 8228. Команда OUT записывает содержимое аккумулятора в порт, адресуемый следующим за командой байтом. Команда IN позволяет считать байт в акку- мулятор. Код Команда D3 OUT pp DB IN pp Периферийным устройствам иногда требуется привлечь внимание микро- процессора. Например, когда вы нажимаете клавишу на клавиатуре, желатель- но, чтобы микропроцессор узнавал об этом сразу. Это реализуется благодаря механизму прерываний — сигналам, поступающим от периферийного устрой- ства на вход INT процессора 8080. Однако после перезапуска микропроцессор 8080 не реагирует на преры- вания. Для разрешения прерываний программа должна выполнить команду EI (Enable Interrupts — разрешить прерывания), а для их запрещения — команду DI (Disable Interrupts — запретить прерывания). Код Команда F3 DI FB EI Глава 19. Два классических микропроцессора 319 Выходной сигнал процессора 8080 INTE означает, что прерывания были разрешены. Когда у периферийного устройства возникает необходимость пре- рвать работу микропроцессора, оно подает на вход INT сигнал, равный 1. В от- вет на него процессор 8080 извлекает из памяти команду, однако управляющие сигналы сообщают о прерывании. В ответ на это периферийное устройство передает процессору 8080 одну из следующих команд. Код Команда Код Команда C7 RST 0 E7 RST 4 CF RST 1 EF RST 5 D7 RST 2 F7 RST 6 DF RST 3 FF RST 7 Эти команды RST (Restart — перезагрузка) аналогичны командам CALL в пла- не того, что текущее значение счетчика команд сохраняется в стеке. Однако после этого RST осуществляет переход к определенным адресам памяти: RST 0 переходит к ячейке 0000h, RST 1 — к ячейке 0008h и так далее вплоть до RST 7, которая совершает переход к ячейке 0038h. В этих ячейках должны содержать- ся фрагменты кода, предусмотренного для обработки прерывания. Например, прерывание от клавиатуры привело к выполнению команды RST 4. Это значит, что начиная с ячейки 0020h в памяти должен храниться некоторый код для считывания байта, введенного с клавиатуры. (В главе 21 я объясню все это.) К настоящему моменту мной описаны 243 кода команд. Существует 12 бай- тов, которые не соответствуют никаким командам: 08h, 10h, 18h, 20h, 28h, 30h, 38h, CBh, D9h, DDh, EDh и FDh. В сумме это дает 255. Но есть еще один код, о котором я должен упомянуть. Код Команда 00 NOP Команда NOP (No Operation — нет операции) заставляет процессор без- действовать. Для чего она нужна? Для заполнения адресного пространства. Как правило, процессор 8080 может выполнять множество команд NOP без каких-либо неприятных последствий. Не буду подробно описывать устройство микросхемы 6800 компании Motorola, поскольку ее конструкция и функционал во многом аналогичны со- ответствующим аспектам процессора 8080. На следующей схеме изображены ее 40 контактов. 320 Код V SS 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 A 12 A 13 A 14 A 15 D 7 D 6 D 5 D 4 D 3 D 2 D 1 D 0 R/W DBE 0 2 TSC RESET A 11 A 10 A 9 A 8 A 7 A 6 A 5 A 4 A 3 A 2 A 1 A 0 V CC BA NM I VM A IRQ 0 1 HALT V SS MC6800 Контакт V SS подключается к земле, V CC — к источнику питания с напря- жением пять вольт. Как и процессор 8080, микросхема 6800 предусматрива- ет 16 выходных адресных сигналов и восемь сигналов для данных, работаю- щих как на ввод, так и на вывод. Есть сигналы RESET и R/W (чтение/запись). На вход IRQ подается сигнал запроса на прерывание (interrupt request). Счита- ется, что система синхронизации в микросхеме 6800 устроена гораздо проще, чем в процессоре 8080. Чего в микросхеме 6800 нет, так это понятия портов ввода/вывода. Адреса всех устройств ввода и вывода принадлежат общему ад- ресному пространству 6800. Микросхема 6800 предусматривает 16-битный счетчик команд, 16-битный указатель стека, 8-битный регистр состояния (для флагов) и два 8-битных ак- кумулятора, называемых A и B. В качестве аккумулятора, а не простого регистра рассматривается В, по- скольку его можно использовать так же, как и A. Однако дополнительных 8-битных регистров в такой микросхеме нет. Вместо этого 6800 предполагает 16-битный индексный регистр, который может использоваться для хранения 16-битного адреса, подобно паре регистров HL в 8080. Для многих команд адрес может вычисляться путем суммирования значения их индексного регистра и байта, который следует за кодом команды. Несмотря на то что микросхема 6800 выполняет примерно те же опера- ции, что и процессор 8080 (загрузка, сохранение, сложение, вычитание, сдвиг, |