Урок ассемблера для ламеров. (продолжение) Сегодня я собираюсь рассказать вам о методах опроса клавиатуры, прерываниях, а также постараюсь исправить свои ошибки,ко- торые допустил в предыдущем номере. 1. Начну с ошибок. В прошлый раз я так страшно загрузил вас описанием директив ассемблера, причём весьма неполным и ошибочным описанием. Но если вы внимательно читали текст об ALASM, то, скорее всего,пропустили этот бред мимо ушей.Дальнейшее относится к тем другим,кто поверил этому бреду и не читал описания.Во избежание. Нельзя сказать,что директива не занима- ет места в памяти.Взять хотя бы DEFB (DB): она, конечно же, не является командой про- цессору,но память жрёт честно:ровно столь- ко,сколько байт информации после неё запи- сано.Или другая директива: DUP 8 RR C RLA EDUP После компиляции этот кусок будет выг- лядеть так: RR C RLA RR C RLA RR C RLA RR C RLA RR C RLA RR C RLA RR C RLA RR C RLA Мало того, что он занимает больше памя- ти,чем просто RR C RLA Ведь при этом вся эта память заполняет- ся сплошь командами процессора! Насчёт директивы ENT я написал полную ерунду.Скажу честно:это потому,что я тогда ещё не разу её не использовал.На самом де- ле эта директива ходит в паре с DISP и ог- раничивает снизу кусок,которому DISP свер- ху приписала работать в других адресах (а не по тому адресу, по которому этот кусок компилируется). Пример: ORG #6000 LD HL,KUSOK LD DE,#4000 PUSH DE LD BC,KUSEND-KUSOK LDIR RET KUSOK DISP #4000 <какая-то программа, сплошь напичканная CALL,JP и т.п.,и потому неперемещаемая> ENT KUSEND Кстати, если метки KUSOK и KUSEND вста- вить внутрь блока DISP-ENT, то эти метки, так же как и все остальные в этом блоке, будут пересчитаны, и,естественно,будут со- держать совсем не те значения,какие бы нам хотелось. В директиве ORG допустимо два парамет- ра, причём если их два,то второй - это но- мер странички,куда мы компилируем. А прог- рамма запускается, между прочим,не по пер- вому ORG, а по последнему! Директива INCLUDE <имя>[,<страничка>] служит для подгрузки модулей для длинных программ. При компиляции ассемблер,натыка- ясь на эту директиву,подгружает соответст- вующий файл (если его ещё нет в памяти), компилирует его, и только затем продолжает компиляцию основной программы с того мес- та, где она была прервана. Директива INCBIN <имя>[,<страничка>] подгружает кодовый файл (кстати,можно ука- зыать расширение через точку) по текущему адресу компиляции, и перемещает этот адрес компиляции на конец этого файла. 2. Опрос клавиатуры. Клавиатура старого ZX Spectrum 48k со- держала 40 клавиш.Остальные клавиши расши- ренной клавиатуры являются их сочетаниями, например BREAK=CAPS/SPACE,EXTEND=CAPS/SYM- BOL,TRUE VIDEO=CAPS/3 и т.д. 40 клавиш собраны в 8 полурядов по 5 клавиш в каждом: #F7FE: 1 2 3 4 5 #EFFE: 6 7 8 9 0 #FBFE: Q W E R T #DFFE: Y U I O P #FDFE: A S D F G #BFFE: H J K L Enter #FEFE:CapsZ X C V #7FFE: B N MSymSpace Через некоторое время я объясню,что это за числа слева, но сперва расскажу о том, как процессор работает с периферийными ус- тройствами (т.е. вообще со всеми устройст- вами, кроме памяти). ____ Устройства выбираются при IORQ=0 (т.е. когда этот сигнал активен).Далее устройст- ва, в_которые записывают,должны проверить, что WR=0 (т.е.активен сигнал записи),а ус- тройства, из_которых принимают данные,про- веряют,что RD=0 (активен сигнал чтения), а потом, все устройства проверяют определён- ную комбинацию адресных сигналов,которая у каждого устройства своя. Например, пищалка (BEEPER) выбирается при A0=0. Откуда берутся эти сигналы?Естественно, с процессора. Комбинация ____ __ IORQ=0, WR=0 вырабатывается при командах OUT,а комбина- ция____ __ IORQ=0, RD=0 - при командах IN. Вроде бы,мы об этих командах ещё не го- ворили. Итак: OUT (<байт>),A - записать аккумулятор в порт, адресом которого служит: A (старший байт)*256+<байт> (младший байт).Содержимое аккумулятора выводится на шину данных. Ад- рес порта, естественно, выводится на шину адреса. OUT (C),<регистр> - записать регистр в порт,адрес которого содержится в регистро- вой паре BC. Регистром может быть:A,B,C,D, E,H,L. OUT (C),0 - записать ноль в порт BC.Ко- манда недокументирована, многими ассембле- рами не поддерживается. На процессоре Z180 приводит к сбросу. IN A,(<байт>) - считать из порта данные в аккумулятор. Адрес порта =A*256+<байт>. IN <регистр>,(C) - считать в регистр данные из порта BC. IN F,(C) - считать данные из порта BC без запоминания. Вместо этого соответству- ющим образом устанавливаются биты флагово- го регистра.(Каким таким образом они уста- навливаются,не знаю.У Ларченко и Родионова не уточняется.) Команда недокументирована. В некоторых ассемблерах она записывается как IN (HL),(C). Команды IN r,(C) и OUT (C),r называются обращением к порту с полной адресацией, в отличие от неполной адресации OUT (b),A.Ко многим портам (в том числе, к музыкальному сопроцессору AY) можно обращаться только с полной адресацией. Существуют также команды блокового об- ращения к портам.Мне как-то не приходилось их использовать. Те загадочные числа, которые я привёл против названий клавиш в полурядах,являют- ся как раз адресами соответствующих пор- тов.А считать из этих портов можно следую- щую информацию: бит 7 - не определён; бит 6 - чтение с магнитофона. На многих компьютерах не реализовано. На некоторых оно реализовано так,что оттуда считываются случайные значения (из-за того,что я забыл замаскировать этот бит по AND,в первом но- мере журнала жутко колбасила стрелка); бит 5 - не определён; биты 4-0 - состояния клавиш полуряда. Если клавиша нажата,считывается 0,а в про- тивном случае - 1.Более младшие биты отно- сятся к более крайним клавишам. Пример использования: START CALL <подпрограмма> LD A,239 ;старший байт полуряда 6-0 IN A,(254) ;считываем данные RRA ;0-й бит переходит в CY JR C,START ;крутим,пока не нажмут 0 На самом деле каждый порт клавиатуры выбирается по двум сброшенным битам:биту 0 и ещё какому-то в пределах от 8 до 15.Если сбросить несколько старших битов,выберутся сразу несколько полурядов, и их содержимые наложатся по AND. Пример: LOOP XOR A ;все биты сброшены IN A,(254) ;опрашиваем все полуряды CPL ;почти эквивалентно комбинации: AND 31 ; AND 31:CP 31, но короче JR Z,LOOP ;пока не нажмут ANY KEY Другим методом опроса клавиш является использование подпрограмм ПЗУ и результа- тов работы прерывания IM 1.Например,проце- дура 8020 сбрасывает CY, если нажат BREAK. А работать с IM 1 можно таким образом: RES 5,(IY+1) ;сброс флага "ANY KEY" NOKEY HALT ;если убрать HALT,может подви- BIT 5,(IY+1) ;снуть на нек. компах! JR Z,NOKEY ;пока не нажмут что-либо LD A,(23560) ;сист.переменная LAST_K В результате в аккумуляторе будет код последней нажатой клавиши. Будьте осторож- ны: если установлен режим CapsLock, то вы будете считывать оттуда сплошь большие бу- квы! А установить CapsLock можно так: LD (IY+48),8 ;сист.переменная FLAGS2 В своём AC Edit я успешно комбинирую оба способа опроса клавиш. 3. Некоторые другие порты. Тот же порт (254), выбранный на запись, отвечает за изменение цвета бордюра,пищал- ку и вывод на магнитофон. Биты у него та- кие: биты 7-5 - не используются; бит 4 - состояние пищалки.Чтобы вызвать звук,необходимо постоянно переключать этот бит; бит 3 - вывод на магнитофон; биты 2-0 - цвет бордюра.У бордюра может быть 8 различных цветов. Яркость в этих цветах отключена.Чтобы получать на бордюре статичные картинки,нужно каждый раз от на- чала кадрового прерывания изменять опреде- лённым образом этот цвет, пока электронный луч прорисовывает экран. Пример: XOR A ;чёрный цвет OUT (254),A ;бордюр станет чёрным Порт 31 (#1F) на чтение повествует о состоянии кемпстон-джойстика: биты 7-5 - всегда равны 0,если джойстик подключен, а в противном случае об их сос- тоянии нельзя сказать ничего определённо- го; бит 4 - огонь (1 - нажат,0 - отпущен); бит 3 - вниз (аналогично); бит 2 - вверх; бит 1 - вправо; бит 0 - влево. Порт 32765 (#7FFD) - порт расширенной памяти (на запись). На чтение невозможен (Кроме нескольких патологов). Назначение битов: биты 7-6 - на 128k не используются; бит 5 - защёлка порта #7FFD. Если этот бит установить, компьютер становится сорок восьмым со всеми вытекающими из этого пос- ледствиями.После сброса всё приходит в но- рму; бит 4 - если 0,то в адресах 0-16383 бу- дет торчать 128-е ПЗУ.Обычно это не нужно, и этот бит устанавливают в 1, тогда в ниж- них адресах будет 48-е ПЗУ; бит 3 - номер экрана: если 0 - то обыч- ный "первый" экран 16384-23295 из странич- ки № 5,иначе - "второй" экран из странички номер 7; биты 2-0 - номер странички памяти,кото- рая будет помещена в область 49152-65535. Здесь нужно остановиться подробнее. Всего у 128k, как можно догадаться, 8 страничек ОЗУ по 16k. От 16384 до 32767 всегда находится страничка № 5,от 32768 до 49151 - страничка № 2,а вот выше - уже лю- бая, какая вам нравится (включая 2 или 5). По выходе из ассемблера устанавливается страничка № 0, сам ассемблер в № 4,монитор в № 7, а текст вашей программы,скорее все- го, в № 6. Страничку № 3 ассемблер исполь- зует под таблицу меток. Итак: Если вы не собираетесь использовать мо- нитор,смело затирайте странички № 3 и № 7. (Только не ассемблируйте на № 3 и на № 7 с #C000 по #C8FF, т. к. она используется как буфер при подгрузке с диска.) Странички №№ 0, 1 безусловно свободны. А если вам стало уж совсем тесно, можете затирать и ассемб- лер вместе с исходником (соответственно №№ 4, 6). , Есть, конечно, Spectrum'ы и с большим объёмом памяти (до 2 Mb),но распространены они мало. Так что писать под них не стоит, тем более что всё равно вы не сможете спо- лна использовать все возможности расширен- ной памяти, не имея таковой. В связи с большим количеством разнооб- разных патологов с разными вариантами де- шифрации порта #7FFD, возникла так называ- емая проблема порта #FD.Название её связа- но с тем,что на компьютерах "Пентагон-128" дешифрация порта расширенной памяти осуще- ствляется всего по 2 битам: A1 и A15,в ре- зультате чего к этому порту стало возможно обращаться по неполной адресации: LD A,23 ;7-я страничка OUT (#FD),A ;записываем её № в порт Вы не находите, что это несколько удоб- нее, чем LD A,23 LD BC,32765 OUT (C),A Вот так многие и подумали, и во многих программах (например,by CodeBusters) стоит именно неполная адресация порта. Разумеет- ся,на некоторых компьютерах такие програм- мы не работают. Например, вышеизложенный пример на "Скорпионе" воспримется как об- ращение к порту #1FFD, а это уже, понятно, совсем другой порт (переключение TURBO, включение в область 0-16383 страницы тене- вого ОЗУ № 8, переключение теневых страниц ОЗУ, ПЗУ и ещё неизвестно чего). Я считаю (и многие другие тоже так счи- тают),что все Спектрумы равноправны,и если программа не работает кое-где из-за такой мелочи, как неполная дешифрация портов, то это плохая программа. Разумеется,некоторые вещи, например, цифровую музыку с высоким качеством, можно реализовать только на не- которых типах Спектрумов ("Пентагон", KAY, ATM и т.п.),но это совсем другая проблема. Это пусть вам дядя Зонов объясняет,зачем в его компьютерах процессор тормозится при общем поле памяти.А если говорить по обще- му счёту,то если поддерживать только "Пен- тагон"(говорю это несмотря на то,что у ме- ня самого "Пентагон"),то синклеристы,поль- зующиеся другими типами Спектрумов (а их не меньше 40%) могут серьёзно обидеться и уйти со Спектрума. Хорошо нам от этого бу- дет? Вот так-то. Между прочим, даже демки теперь работают на "Скорпионах". Учитесь! Порты 65533 (#FFFD),49149 (#BFFD) отве- чают за доступ к музыкальному сопроцессору AY.У сопроцессора 16 регистров разного на- значения. Для того,чтобы записать что-то в какой-либо из них, нужно заслать его номер в порт 65533, а потом данные,предназначен- ные для этого регистра, спустить в порт 49149.Если надо,наоборот,считать данные,то после записи № регистра в порт 65533 нужно считать данные из того же порта 65533. Все анализаторы громкости во всяких там boot' ах основаны именно на чтении из AY. Назна- чение отдельных регистров: №№ 0, 1 - содержит 12-разрядный период (не частоту,а именно период!) ноты в кана- ле A. Причём регистр 1 - старший; №№ 2, 3 - период канала B; №№ 4, 5 - период канала C; № 6 - пятиразрядный период шума; № 7 - управление звуком: биты 6,7 - указывают направление об- мена по параллельным портам, и посему нас не интересуют; биты 3-5 - если 1, то запрещён вывод шума соответственно в канал A,B,C; биты 0-2 - если 1, то запрещён вывод ноты в канал A,B,C. Зато взамен,если мани- пулировать громкостью в соответствующем канале, можно получить цифровую музыку! А если включена огибающая, то она будет зву- чать сама по себе! № 8 - 4-битная громкость канала A.Заме- тьте, что шкала громкости не линейная, а, скорее,логарифмическая.Если установить 4-й бит этого регистра,то громкости уже не бу- дет, а взамен будет накладываться огибаю- щая; №№ 9,10 - аналогично для каналов B,C; №№ 11,12 - период огибающей. При проиг- рывании звука на огибающей используется только младший байт (№ 11); № 13 - 4-битная форма огибающей: 0 - плавное затухание до нуля; 12 - пилообразный звук; 13 - плавное нарастание до максимума; 14 - зигзагообразный звук на октаву ниже, чем 12; №№ 14,15 - используются для обмена по параллельным портам. Конечно, вряд ли вы будете сами играть музыку через прямое обращение к AY,так что в качестве примера покажу только, как вык- лючить звук: LD DE,#E00 LD C,#FD OFF0 LD B,#FF DEC D OUT (C),D ;выбираем № регистра LD B,#BF OUT (C),E ;записываем в него ноль JR NZ,OFF0 ;и так до 0-го регистра 4. Прерывания. Каждые 20 миллисекунд (1/50 секунды) на процессор приходят кадровые прерывания.Это выражается в том,что на ножку INT подаётся отрицательный импульс. Длина импульса, как водится, у каждого патолога своя,но не бу- дем об этом. Если во время прихода импульса были включены прерывания (командой EI,выключаю- тся же они командой DI), то, в зависимости от текущего режима прерываний, процессор начнёт выполнять: а) (режим IM 0) команду, которая пришла с шины данных (не используется); б) (режим IM 1) подпрограмму RST 56; в) (режим IM 2) подпрограмму, адрес ко- торой он прочитает из ячейки с адресом: I*256+<шина данных>,где I - регистр векто- ра прерываний. В любом случае перед входом в подпрог- рамму обработки прерываний процессор отк- лючает прерывания. В настоящий момент нас более всего ин- тересует режим IM 2, т.к.в этом режиме мо- жно вставить собственный обработчик. Поскольку состояние шины данных не оп- ределено в общем случае (т.е. не на всех компьютерах оттуда считывается стабильно 255),то желательно,чтобы адрес обработчика не зависел от состояния шины данных :).Это достигается однородным заполнением одина- ковыми числами так называемой таблицы пре- рывания, длиной 257 байт (для каждого из 256 возможных состояний ШД из таблицы счи- тываются 2 одинаковых байта).Причём табли- цу эту следует размещать в так называемой быстрой памяти (странички № 0 или № 2), а то некоторые патологи могут подвиснуть... Пример: MUZ EQU #C000 ORG MUZ + INCBIN "MUSIC" ORG #6000 DI CALL MUZ ;инициализируем музыку LD HL,#FE00 LD A,H LD I,A ;I=#FE LD DE,#FE01 LD BC,#100 LD (HL),#5C LDIR ;заполняем таблицу прерывания LD L,(HL) LD H,L ;HL=#5C5C LD (HL),195 ;JP INC HL LD (HL),IMER INC HL LD (HL),'IMER IM 2 EI <далее программа будет работать под музыку> IM 1 ;выключаем IM 2 JP MUZ ;выключаем музыку и выходим IMER PUSH AF PUSH BC PUSH DE PUSH HL PUSH IX PUSH IY CALL MUZ+5 ;вызываем музыку POP IY POP IX POP HL POP DE POP BC POP AF EI RET То есть,если повесить на прерывание ка- кое-либо действие (например, проигрывание музыки), то программа уже не должна будет за этим следить, и мы получаем некоторое подобие многозадачности (много-,а не дву-, потому что никто не мешает нам повесить на прерывание несколько разных задач, напри- мер, музыку и мультиколор). Со включенным прерыванием нельзя вхо- дить в TR-DOS, иначе при первом же вызове прерывания TR-DOS исчезнет, и на её месте окажется BASIC.И при возвращении из преры- вания мы попадём немного не туда :). Спо- соб, как обойти это,указан в книге "TR-DOS для пользователей и программистов". На са- мом деле всё это не нужно.Просто отключай- те прерывания при входе в TR-DOS. Если не хватает места под таблицу пре- рывания, поместите её в медленную память, например,по адресу #5B00.Но учтите,что то- гда ваша программа будет работать не вез- де.Если и в медленной памяти нет места,мо- жете оставить от таблицы всего 2 байта по адресу I*256+255 (так сделано в TLW2).Этим вы ещё сильнее уменьшаете число своих по- льзователей.Самый последний вариант: I=59. Тогда таблица (длиной 257 байт для 48k,или 2 байта для 128k) будет в ПЗУ, а процедура обработки прерываний - по адресу 65535.По- ставьте туда JR, а по адресу 65524 (куда указывает JR) поставьте обработчик. Теперь работоспособность вашей программы, помимо модели компьютера,ограничена ещё и версией ПЗУ... 5. Полезные советы. Лучше всего начинать набирать программу со строк: ORG #6000 LD (QUIT+1),SP QUIT LD SP,0 LD IY,23610 IM 1 LD HL,10072 EXX EI RET END Кто знает,какие регистры может испорти- ть ваша программа, пока она ещё глючит? А метка END позволяет вам всегда узнать дли- ну программы.Для этого после ассемблирова- ния в командном режиме введите: COUNT END-#6000 Советую также поставить в программу по- дпрограмму печати 16-разрядных чисел и вы- зывать её время от времени в главном цикле работы программы. Это позволит вам контро- лировать зависание. Подпрограмму см.в раз- деле "Этюды". Пишите программу красиво и удобно для исправления.Чаще используйте CALL.Выделяй- те законченный функциональный блок в под- программу, даже если он вызывается только один раз.Сохраняйте регистры в подпрограм- мах.Не думайте о перемещаемости,используй- те JP вместо JR. Все адреса таблиц,музыки,спрайтов и др. заносите в EQU.Это позволит вам легко сме- нить эти адреса, если что. Последние опыты показали, что даже номера используемых страниц памяти могут измениться, и их тоже надо заносить в EQU. Когда ваша программа превысит 4k (а у меня уже 3 таких программы), её текст нач- нёт не помещаться в 16k-буфере. Придётся выделить модуль и подгружать его по дирек- тиве: INCLUDE "MYUNIT",1 Подгружаться он будет в другую странич- ку, и это позволит вам одновременно редак- тировать оба текста.Для модуля следует от- бирать подпрограммы, которые вам не прихо- дилось исправлять долгое время (я называю их "бессмертными"). Подгружайте INCBIN'ом данные в странич- ки разве что только во время отладки.Лучше всего подгружать их непосредственно на хвост программы,а потом перекидывать LDIR- 'ом. Тогда программу будет легче отгрузить в откомпилированном виде и запаковать.Сто- ит ли говорить о том,что прогу,раскиданную по страничкам,запаковать вообще ни в одном упаковщике нельзя? Продолжение следует...