#

Ассемблер - Краткий урок ассемблера для ламеров (продолжение)

<b>Ассемблер</b> - Краткий урок ассемблера для ламеров (продолжение)
       Урок ассемблера для ламеров.
            (продолжение)

   Сегодня  я  собираюсь  рассказать вам о
методах  опроса клавиатуры, прерываниях, а
также постараюсь исправить свои ошибки,ко-
торые допустил в предыдущем номере.

          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-
'ом. Тогда программу будет легче отгрузить
в откомпилированном виде и запаковать.Сто-
ит ли говорить о том,что прогу,раскиданную
по страничкам,запаковать вообще ни в одном
упаковщике нельзя?

          Продолжение следует...


Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Похожие статьи:
BBS - список станций BBS ZXNet.
Вступление - Число cheat'ов в COSG перевалило за сотню!
Navigation - новая крутая оболочка! Как с ней управляться?
News - Hippiman changed his residence, then started to make a game, John Silver bought studio speakers, first ATM 8.00 board was finally
Ретро - Чернокнижник #1.

В этот день...   21 ноября