\/d П Р И Л О Ж Е Н И Е 1. MSX-BASIC И МАШИННЫЙ ЯЗЫК \/d- Покажите, как операторы языка ассемблера можно чередовать с операторами данного языка. Д.Уолш. Руководство по созданию документации для математического обеспечения 1.1. С в я з ь п р о г р а м м ы на MSX-BASIC с п о д п р о г р а м м а м и в м а ш и н н ы х к о д а х Перед выполнением оператора MSX-BASIC выполняется проверка его правиль- ности,и в случае обнаружения ошибок на экран выводятся сообщения. Выполни- мый оператор сначала преобразуется в последовательность команд,которые мо- жет "понять" процессор. Эти подпрограммы заранее помещены в ROM на этапе изготовления компьютера. Интерпретация операторов MSX-BASIC занимает определенное время; в неко- торых случаях, однако, это время должно быть сокращено (например, для иг- ровых программ). Это можно сделать двумя способами: 1) основная программа пишется на MSX-BASIC, а в ситуациях, когда интер- претатор MSX-BASIC не обеспечивает нужного быстродействия, используются подпрограммы, написанные в машинных кодах; 2) вся программа пишется на машинном языке. Отметим, что существует обширная литература по этому кругу проблем, и читатель, овладевший языком MSX-BASIC и желающий научиться хорошо програм- мировать на машинном языке, может обратиться к этой литературе [45,66,67]. Для обращения к подпрограммам в машинных кодах из программ на языке MSX-BASIC используются оператор DEFUSR и функция USR. Оператор DEFUSR определяет начальный адрес машинной подпрограммы. Синтаксис оператора: ┌────────────────────────┐ │ DEFUSR[n] = а д р е с │ , └────────────────────────┘ где: DEFUSR("DEFine USing Routine"-"определить используемую подпрограмму") - служебные слова; n - целое число,принадлежащее [0,9] и указывающее номер подпрограммы (по умолчанию значение n равно 0); а д р е с - арифметическое выражение, целая часть значения которого определяет начальный адрес подпрограммы. Таким образом, в программе на языке MSX-BASIC может использоваться до д е с я т и подпрограмм в машинных кодах одновременно.Они различаются по номеру (от 0 до 9), который записываются вслед за служебным словом DEFUSR. Например, оператор DEFUSR1=&HA000 указывает, что начальным адресом программы с номером 1 является &HA000 . ┌───────────────┐ Функция │ USR[n](α) │ , └───────────────┘ где: USR("USer"-"пользователь") - служебное слово; n - десятичная цифра (0,1,2,3,4,5,6,7,8,9), причем по умолчанию n=0; α - арифметическое или строковое выражение (которое, кстати,может от- сутствовать), позволяет в ы п о л н и т ь подпрограмму, написанную на машинном языке. П р и м е р 1. Пусть, например, одна машинная подпрограмма начинает- ────────────── ся с адреса &HD000, а другая - с адреса &HD100 : DEFUSR0=&HD000:DEFUSR1=&HD100 Теперь выполнить эти подпрограммы можно при помощи операторов: A=USR0(A):A=USR1(A) Значение выражения α передается подпрограмме, написанной в машинных ко- дах. При вызове подпрограммы число байтов,занимаемое этим значением, зано- сится в регистр A микропроцессора Z80 и в ячейку памяти с адресом &HF663 . ┌────────────────────────┬────────────────────────────────────┐ │ Содержимое регистра А │ Тип значения выражения α │ ├────────────────────────┼────────────────────────────────────┤ │ 2 │ Целочисленный │ │ 4 │ Вещественный с одинарной точностью │ │ 8 │ Вещественный с двойной точностью │ │ 3 │ Строковый │ └────────────────────────┴────────────────────────────────────┘ Если аргументом функции USR является н у л ь , то в подпрограмму в ма- шинных кодах ничего не передается! Отметим, что значение, возвращаемое функцией USR, представляет собой значение ее аргумента. Если α - арифметическое выражение, то адрес его значения хранится в регистре HL . Если же α - строковое выражение, то ссылка на адрес, по которому хра- нится значение α, содержится в регистре DE. Ниже мы расскажем Вам и о других способах передачи аргументов машинным подпрограммам. П р и м е р ы Некоторые люди учатся читая, другие - делая. Преуспевающие делают и то, и другое. П.Браун Примеры, немногочисленные, лаконичные,приве- денные к месту, сообщают рассуждению блеск, глубину и убедительность, но оно становится невразумительным, если примеров и подробно- стей чересчур много... Л.К.де Вовенарг. Размышления и максимы П р и м е р 1. Загрузка регистров A и BC константами. ────────────── 10 CLEAR 200,&HD000:DEFUSR=&HD000 20 DATA 3E,C3 :'LD A,C3h 30 DATA 01,A1,56 :'LD BC,56A1h 40 DATA 32,10,D0 :'LD (D010h),A 50 DATA ED,43,11,D0 :'LD (D011h),BC 60 DATA C9,"RET" :'RET 80 READ A$ 90 IF A$="RET" THEN 120 100 POKE &HD000+T,VAL("&h"+A$) 110 T=T+1:GOTO 80 120 A=USR(A) 130 PRINT "A=";HEX$(PEEK(&HD010)) 140 PRINT "BC=";HEX$(PEEK(&HD012));HEX$(PEEK(&HD011)) run A=C3 BC=56A1 Ok П р и м е р 2. Пересылка содержимого регистра C в регистр A . ────────────── 10 CLEAR 200,&HD000:DEFUSR=&HD000 20 DATA 0E,C3 :'LD C,C3h 30 DATA 79 :'LD A,C 40 DATA 32,10,D0 :'LD (D010h),A 50 DATA C9,"RET" :'RET 70 READ A$:IF A$="RET" THEN 110 90 POKE &HD000+T,VAL("&h"+A$) 100 T=T+1:GOTO 70 110 A=USR(A) 120 PRINT "C=A= ";HEX$(PEEK(&HD010)) run C=A= C3 Ok П р и м е р 3. Загрузка содержимого регистра A в память по адресу, ────────────── указанному в регистре BC. 10 CLEAR 200,&HD000:DEFUSR=&HD000 20 DATA 3E,C3 :'LD A,C3h 30 DATA 01,10,D0 :'LD BC,D010h 40 DATA 02 :'LD (BC),A 50 DATA C9,"RET" :'RET 70 READ A$:IF A$="RET" THEN 110 90 POKE &HD000+T,VAL("&h"+A$):T=T+1:GOTO 70 110 A=USR(A) 120 PRINT "A=";HEX$(PEEK(&HD010)) run A=C3 Ok П р и м е р 4. Проиллюстрируем работу команды EX DE,HL . ────────────── 10 CLEAR 200,&HD000:DEFUSR=&HD000 20 DATA 11,C3,98 :'LD DE,98C3h 30 DATA 21,A1,56 :'LD HL,56A1h 40 DATA ED,53,30,D0 :'LD (D030h),DE 50 DATA ED,63,32,D0 :'LD (D032h),HL 60 DATA EB :'EX DE,HL 70 DATA ED,53,34,D0 :'LD (D034h),DE 80 DATA ED,63,36,D0 :'LD (D036h),HL 90 DATA EB :'EX DE,HL 100 DATA ED,53,38,D0 :'LD (D038h),DE 110 DATA ED,63,3A,D0 :'LD (D03Ah),HL 120 DATA C9,"RET" :'RET 140 READ A$ 150 IF A$="RET" THEN 180 160 POKE &HD000+T,VAL("&h"+A$) 170 T=T+1:GOTO 140 180 A=USR(A) 190 PRINT "До команды EX DE,HL:" 200 PRINT " DE=";HEX$(PEEK(&HD031));HEX$(PEEK(&HD030)) 210 PRINT " HL=";HEX$(PEEK(&HD033));HEX$(PEEK(&HD032)) 220 PRINT "После команды EX DE,HL:" 230 PRINT " DE=";HEX$(PEEK(&HD035));HEX$(PEEK(&HD034)) 240 PRINT " HL=";HEX$(PEEK(&HD037));HEX$(PEEK(&HD036)) 250 PRINT "После еще одной команды EX DE,HL:" 260 PRINT " DE=";HEX$(PEEK(&HD039));HEX$(PEEK(&HD038)) 270 PRINT " HL=";HEX$(PEEK(&HD03B));HEX$(PEEK(&HD03A)) run До команды EX DE,HL: DE=98C3 HL=56A1 После команды EX DE,HL: DE=56A1 HL=98C3 После еще одной команды EX DE,HL: DE=98C3 HL=56A1 Ok П р и м е р 5. Сложение целых чисел, принадлежащих отрезку [0,255]. ────────────── 10 CLEAR 100,&HD000 'Очистка памяти 20 POKE &HD000,9 'Запись в память первого операнда 30 POKE &HD001,9 'Запись в память второго операнда 40 AD=&HD003 'Адрес начала подпрограммы 50 READ T$:IF T$="z" THEN 90 70 POKE AD,VAL("&h"+T$):AD=AD+1:GOTO 50 90 DEFUSR=&HD003:H=USR(0) 105 PRINT PEEK(&HD002) 'Печать результата 110 DATA 3A,00,D0 :'LD A,(D000) ; (D000h) ──▶ А 120 DATA 47 :'LD B,A ; (A) ──▶ B 130 DATA 3A,01,D0 :'LD A,(D001) ; (D001h) ──▶ А 140 DATA 80 :'ADD A,B ; (A)+(B) ──▶ A 150 DATA 32,02,D0 :'LD (D002),A ; (А) ──▶ (D002h) 160 DATA C9,"z" :'RET ; Возврат из подпрограммы П р и м е р 6. Умножение содержимого двух ячеек памяти. ────────────── 10 CLEAR 200,&HA000 20 INPUT A,B:POKE &HA100,A:POKE &HA101,B 'Ввод начальных данных 30 FOR I=0 TO 15:READ R$:POKE &HA000+I,VAL("&h"+R$):NEXT 40 DEFUSR=&HA000:Z=USR(0) 50 PRINT PEEK(&HA102) 60 DATA 3A,00,A1 :' LD A,(A100h) 70 DATA 47 :' LD B,A 80 DATA 05 :' DEC B 90 DATA 3A,01,A1 :' LD A,(A101h) 100 DATA 57 :' LD D,A 110 DATA 82 :' ADD A,D ◀────┐ 120 DATA 10,FD :' DJNZ $-1 ────┘ 130 DATA 32,02,A1 :' LD (A102h),A 150 DATA C9 :' RET П р и м е р 7. Нахождение суммы целых чисел от 1 до 10 . ────────────── 10 CLEAR 100,&HD000:DEFUSR=&HD000:AD=&HD000 40 READ A$:IF A$="Z"THEN 80 60 POKE AD,VAL("&h"+A$):AD=AD+1:GOTO 40 80 A=USR(0):PRINT"Сумма:"PEEK(&HD00C):END 110 DATA 06,0A :' LD B,0Ah 120 DATA 3E,00 :' LD A,00h 130 DATA 80 :' ADD A,B ;◀──┐ 140 DATA 10,FD :' DJNZ D004h ;───┘ 150 DATA 32,0C,D0 :' LD (D00C),A 160 DATA C9,"Z" :' RET П р и м е р 8. Нахождение большего из двух чисел. ────────────── 10 PRINT"Найду большее из двух чисел (0÷255)" 20 INPUT"Первое число ";N1:INPUT"Второе число ";N2 40 POKE &HC000,N1:POKE &HC001,N2 50 CLEAR 200,&HD000:DEFUSR=&HD000:I=&HD000 70 READ A$:IF A$="z" THEN 110 90 POKE I,VAL("&h"+A$):I=I+1:GOTO 70 110 A=USR(0) 120 PRINT"Большее число:";PEEK(&HC003):END 130 DATA 3A,00,C0 :'LD A,(C000h) 140 DATA 47 :'LD B,A 150 DATA 3A,01,C0 :'LD A,(C001h) 160 DATA B8 :'CP B 170 DATA F2,0C,D0 :'JP P,D00Ch 180 DATA 78 :'LD A,B 190 DATA 32,03,C0 :'LD (C003h),A 200 DATA C9,"z" :'RET Напомним, что подрограмма в машинных кодах вызывается следующим обра- зом: ┌────────────────────────────────────────────────┐ │ П е р е м е н н а я = USR[n](а р г у м е н т) │ , └────────────────────────────────────────────────┘ где n - целое число, принадлежащее отрезку [0,9], которое указывает но- мер подпрограммы в машинных кодах. Значение а р г у м е н т а передается подпрограмме в машинных кодах. При вызове подпрограммы число байтов, занимаемое этим значением, однознач- но связанное с его типом, заносится в регистр A микропроцессора Z80 и в ячейку памяти F663h. Возможны следующие значения: 2 - целочисленные, 4 - вещественные с одинарной точностью, 8 - вещественные с двойной точностью, 3 - строковые. Адрес числовой п е р е м е н н о й хранится в регистре HL; адрес строковой п е р е м е н н о й находится в регистре DE. П р и м е р 9. ───────────── 10 CLEAR 200,&HF000 20 DATA ED,63,02,F3 :'LD (F302h),HL 30 DATA 3A,F8,F7 :'LD A,(F7F8h) 40 DATA 4F :'LD C,A 50 DATA 81 :'ADD A,C 60 DATA 32,00,C0 :'LD (C000h),A 70 DATA C9,"RET" :'RET 80 DEFUSR=&HF000 90 READ Z$:POKE &HF000+T,VAL("&H"+Z$) 100 T=T+1:IF Z$<>"RET" THEN 90 110 A=USR(6) 'Обратите внимание на то, что аргумент имеет целый тип! 120 PRINT" В регистре HL находится число: &h";HEX$(PEEK(&HF303));RIGHT $("00"+HEX$(PEEK(&HF302)),2) 125 PRINT" Это есть адрес аргумента функции USR" 130 PRINT" Проверяем этот факт:... и вправду ";LEFT$(HEX$(PEEK(&HF7F7) ),1) 140 PRINT" Результат работы подпрограммы в машинных кодах: ";PEEK(&HC0 00) run В регистре HL находится число: &hF7F6 Это есть адрес аргумента функции USR Проверяем этот факт:... и вправду 6 Результат работы подпрограммы в машинных кодах: 12 Ok П р и м е р 10. ─────────────── 10 CLEAR 200,&HF000 20 DATA 32,00,F3 :'LD (F300h),A 30 DATA 3A,63,F6 :'LD A,(F663h) 40 DATA 32,01,F3 :'LD (F301h),A 50 DATA ED,53,02,F3 :'LD (F302h),DE 60 DATA C9,"RET" :'RET 70 DEFUSR=&HF000 80 READ Z$:POKE &HF000+T,VAL("&h"+Z$) 90 T=T+1:IF Z$<>"RET" GOTO 80 100 A$="MSX":A$=USR(A$) 'Обратите внимание на то, что тип аргумента - строковый ! 110 PRINT"В регистре А:";PEEK(&HF300) 120 PRINT"В ячейке F663h:";PEEK(&HF301) 130 PRINT"Адрес переменной:";HEX$(PEEK(&HF303)); RIGHT$("00"+HEX$(PEEK(&HF302)),2) run В регистре А: 3 В ячейке F663h: 3 Адрес переменной:8183 Ok ?hex$(peek(&h8185));hex$(peek(&h8184)) 80E7 Ok ?chr$(peek(&h80E7)) M Ok Итак, информация о типе переменной A$ хранится в ячейке с адресом &HF663 , а ссылка на адрес переменной A$ (&H8183) хранится в регистре DE. Если вы не тот, кто наверху, значит, вы тот, кто внизу. С.Поттер П р и м е р 11. Сортировка целочисленного массива , содержащего 5 ─────────────── элементов. 10 CLEAR 200,&HA000:DEFUSR=&HA000:AD=&HA000 20 READ A$:IF A$="z" THEN 50 30 POKE AD,VAL("&h"+A$) 40 AD=AD+1:GOTO 20 50 FOR I=0 TO 4:INPUT A:POKE &HB000+I,A:NEXT 60 A=USR(0) 70 FOR I=0 TO 4:PRINT PEEK(&HC000+I):NEXT 80 END 90 DATA 0E,00 :'LD C,00 100 DATA 11,00,C0 :'LD DE,C000 110 DATA 21,00,B0 :'LD HL,B000 ◀────┐ 120 DATA 06,05 :'LD B,05 │ 130 DATA 7E :'LD A,(HL) ◀─┐ │ 140 DATA B9 :'CP C │ │ 150 DATA C2,11,A0 :'JP NZ,A011 │ ──┐ │ 160 DATA 12 :'LD (DE),A │ │ │ 170 DATA 13 :'INC DE │ │ │ 180 DATA 23 :'INC HL │ ◀──┘ │ 190 DATA 10,F6 :'DJNZ A00A ──┘ │ 200 DATA 79 :'LD A,C │ 210 DATA FE,FF :'CP FF │ 220 DATA CA,1E,A0 :'JP Z,A01E ──┐ │ 230 DATA 0C :'INC C │ │ 240 DATA C3,05,A0 :'JP A005 ──│─────┘ 250 DATA C9,"z" :'RET ◀──┘ П р и м е р 12. Вычисление факториала "маленьких" целых чисел ─────────────── 10 CLS:KEYOFF:PRINT"Найду факториал заданного Вами целого числа" 20 INPUT"Введите целое число, меньшее 6";F 30 POKE &HC000,F 40 IF F=0 OR F=1 THEN POKE &HC003,1:GOTO 90 50 CLEAR 200,&HD000:DEFUSR=&HD000:I=&HD000 60 READ A$:IF A$="z" THEN 80 70 POKE I,VAL("&h"+A$):I=I+1:GOTO 60 80 A=USR(0) 90 PRINT "Факториал "PEEK(&HC000)"=" PEEK(&HC003):END 100 DATA 3A,00,C0 :'LD A,(C000h) 110 DATA FE,02 :'CP 2h 120 DATA 28,0A :'JR Z,$+12 ───────────┐ 130 DATA 47 :'LD B,A │ 140 DATA 05 :'DEC B │ 150 DATA 05 :'DEC B │ 160 DATA 48 :'LD C,B ◀───────┐ │ 170 DATA 57 :'LD D,A │ │ 180 DATA 82 :'ADD A,D ◀────┐ │ │ 190 DATA 10,FD :'DJNZ $-1h ────┘ │ │ 200 DATA 41 :'LD B,C │ │ 210 DATA 10,F8 :'DJNZ $-6h ───────┘ │ 220 DATA 32,03,C0 :'LD (C003h),A ◀───────┘ 230 DATA C9,"z" :'RET П р и м е р 13. Умножение шестнадцатиразрядных целых чисел без знака. ─────────────── 20 CLEAR 200,&HF000 40 INPUT"Первое целое число";N1% 50 INPUT"Второе целое число";N2% 60 POKE &HF000,PEEK(VARPTR(N1%)):POKE &HF001,PEEK(VARPTR(N1%)+1) 70 POKE &HF002,PEEK(VARPTR(N2%)):POKE &HF003,PEEK(VARPTR(N2%)+1) 80 DEFUSR=&HF006 90 DATA ED,6B,00,F0 :' ARG1: LD HL,(F000) 100 DATA ED,5B,02,F0 :' ARG2: LD DE,(F002) 110 DATA 06,10 :' LD B,10 120 DATA 4A :' LD C,D ; Пересылка множителя 130 DATA 7B :' LD A,E 140 DATA EB :' EX DE,HL ; Пересылка множимого 150 DATA 21,00,00 :' LD HL,0000 ; Установка частичного ре- ; зультата в исходное сос- ; тояние 160 DATA CB,39 :' MLOOP:SRL C ; Сдвиг множителя вправо 170 DATA 1F :' RRA ; Наименее значащий раз- ; ряд к переносу 180 DATA 30,01 :' JR NC,NOADD-S; Если нет переноса, то пропуск суммирования 190 DATA 19 :' ADD HL,DE ; Иначе суммирование мно- ; жимого в частичный резу- ; льтат 200 DATA EB :' NOADD:EX DE,HL ; Сдвиг множимого влево 210 DATA 29 :' ADD HL,HL ; при умножении на 2 220 DATA EB :' EX DE,HL 230 DATA 10,F5 :' DJNZ MLOOP-S ; Повторение до последнего ; разряда 240 DATA ED,63,04,F0 :' REZ: LD (F004),HL 250 DATA C9 :' RET 260 FOR T=0 TO 31:READ Z$:POKE &HF006+T,VAL("&h"+Z$):NEXT 270 X=USR(X) 'Выполним умножение чисел N1% и N2% 280 MULT=256*PEEK(&HF005)+PEEK(&HF004) 290 IF MULT>32767 THEN MULT=MULT-65536! 300 PRINT"Ваш результат:";MULT:END 1.2. И с п о л ь з о в а н и е п о д п р о г р а м м BIOS Когда книга сталкивается с головой - и при этом раздается глухой звук, разве всегда виновата книга. Г.Лихтенберг 1.2.1. П о д п р о г р а м м ы BIOS б е з п а р а м е т р о в Подпрограммы BIOS ("Basic Input-Output System") имеют фиксированные ад- реса ROM, которые называются в х о д н ы м и точками. ┌───────────────────────────────────────────────────────────────────────┐ │ Прежде чем Вы начнете разработку собственных подпрограмм,написанных в │ │ машинных кодах,рекомендуем тщательно изучить, что делают подпрограммы │ │ BIOS . │ └───────────────────────────────────────────────────────────────────────┘ Достаточно одного взгляда на описание подпрограмм BIOS, чтобы заметить, что некоторые подпрограммы для работы н е т р е б у ю т ввода парамет- ров. Проиллюстрируем использование таких подпрограмм. П р и м е р 1. И н и ц и а л и з а ц и я системы. ───────────── ┌────────────────────────┐ │ DEFUSR=0:A=USR(0) │ └────────────────────────┘ Это "убийственная" инициализация - уничтожается все содержимое RAM (анало- гично нажатию кнопки "RESET"). П р и м е р 2. И н и ц и а л и з а ц и я значений функциональных кла- ───────────── виш. Если Вам время от времени необходимо возвращение к стандартному определению значений, присвоенных функциональным клавишам, то поступите следующим образом: 10 KEY 1,"AAAA":KEY 2,"BBBB" 20 DEFUSR=&H3E 'Запомните адрес &H3E ! 30 IF INKEY$="" THEN 30 40 A=USR(0):KEY ON Программа BIOS с именем INIFNK по адресу &H3E переопределяет функциона- льные клавиши. Однако следует выполнить оператор KEY ON, чтобы увидеть ре- зультат на экране дисплея. П р и м е р 3. В к л ю ч е н и е и в ы к л ю ч е н и е экрана. ───────────── Если Вы хотите моментально вывести большое количество информации на экран, то можно вначале "выключить" экран, затем выполнить операторы вывода информации, а только после этого "включить" экран. ┌─────────────────────────────────────────────────────────────────────┐ │ Подпрограмма BIOS с именем DISSCR, расположенная по адресу &H41 ,│ │ "в ы к л ю ч а е т" экран, тогда как подпрограмма с именем ENASCR,│ │ расположенная по адресу &H44 , "в к л ю ч а е т" его. │ └─────────────────────────────────────────────────────────────────────┘ П р и м е р 4. 10 DEFUSR=&H41:DEFUSR1=&H44 ────────────── 20 SCREEN 0:A=USR(0) 40 FOR I=0 TO 1200:PRINT "A";:NEXT 70 A=USR1(0) П р и м е р 5. Очистка буфера клавиатуры ────────────── 10 CLEAR 200,&HD000:DEFUSR=&HD000 20 READ A$:IF A$="RET" THEN 50 30 POKE &HD000+T,VAL("&h"+A$) 40 T=T+1:GOTO 20 50 FOR I=0 TO 1000:NEXT' З а д е р ж к а 60 A=USR(0) 61 PRINT CHR$(PEEK(&H9000)) :'Вывод п е р в о г о нажатого символа 70 DATA CD,9F,0 :'Ожидание нажатия клавиши 80 DATA 32,00,90 :'Код нажатой клавиши ──▶ по адресу 9000 90 DATA CD,56,1 :'CALL KILBUF ∗ BIOS 0156 100 DATA C9 :'RET ∗ Вход: Нет Выход: Нет 110 DATA "RET" :' ∗ Изменения: регистр HL Приведем еще ряд примеров: 6) инициализация системы 10 CALL NETEND 'Для компьютеров MSX-2 20 DEFUSR=0:A=USR(A) 7) установка режима SCREEN 10 DEFUSR0=&H6C:DEFUSR1=&H6F 30 DEFUSR2=&H72:DEFUSR3=&H75 50 INPUT"Режим";N 60 IF N=0 THEN A=USR0(A) 70 IF N=1 THEN A=USR1(A) 80 IF N=2 THEN A=USR2(A) 90 IF N=3 THEN A=USR3(A) 8) возврат в текстовый режим 10 DEFUSR=&HD2:A=USR(A) 9) инициализация Таблицы шаблонов и Таблицы атрибутов спрайтов 10 DEFUSR=&H69:A=USR(A) 10) инициализация Программируемого Звукового Генератора (PSG) 10 DEFUSR=&H90:A=USR(A) 11) подача звукового сигнала 10 DEFUSR=&HC0:A=USR(A) 12) очистка экрана 10 DEFUSR=&HC3:A=USR(A) 13) вывод значений функциональных клавиш на экран дисплея 10 DEFUSR=&HCF:A=USR(A) 14) отмена вывода значений функциональных клавиш на экран дисплея 10 DEFUSR=&HCC:A=USR(A) Может ли лакомка не есть, если стол перед ним ломится от яств? Пусть же он утолит, наконец, голод и жажду! Голландская народная песня 1.2.2. П о д п р о г р а м м ы BIOS, т р е б у ю щ и е п а р а м е т р о в Заметим, что большинство подпрограмм BIOS требуют для своей работы пе- редачи параметров, значения которых должны быть помещены в определенные регистры перед вызовом подпрограмм. Такие подпрограммы обычно запускаются программой,написанной в машинных кодах и состоящей из: α) записи исходных данных в определенные регистры микропроцессора; β) вызова нужной подпрограммы BIOS. Ознакомим Вас с приемами написания подпрограмм на машинном языке, тре- бующих для своей работы передачи параметров. П р и м е р 1. Программа, включающая и выключающая индикатор CAPS ───────────── (ввод заглавных букв). Подпрограмма BIOS с именем CHGCAP,расположенная по адресу &H0132 выклю- чает индикатор "CAPS", если регистр A процессора содержит ненулевое значе- ние, и включает индикатор "CAPS", если этот регистр содержит 0. Сначала опишем алгоритм программы,которая позволяет включить индикатор "CAPS": α) загрузить 0 в регистр A; β) перейти к подпрограмме с именем CHGCAP. Запишем алгоритм на ассемблере: LD A,0 ;Загрузим 0 в регистр А JP 0132 ;Перейдем к адресу &Н0132 А теперь приведем запись программы на машинном языке (в шестнадцатерич- ных кодах): 3E, 00 LD A,0 C3, 32, 01 JP 0132h и на языке MSX-BASIC: 10 CLEAR 200,&HD000 ' Резервирование области в RAM 20 AD=&HD000 ' Запись текста программы 30 READ A$ ' в машинных кодах в эту область 40 IF A$="Z" THEN 100 ' посредством "любимых" нами 50 A=VAL("&H"+A$):POKE AD,A ' операторов 70 AD=AD+1:GOTO 30 ' POKE 100 DEFUSR=&HD000:A=USR(0) ' Запуск программы 220 DATA 3E,00 :' LD A,0 230 DATA C3,32,01 :' JP 0132 240 DATA "Z" Эта программа включает индикатор "CAPS". Выключите его вручную и введи- те команду: PRINT USR(0) ┌──────────────────────────────────────────────────────────────────────┐ │ Заметьте, что подпрограмма с именем CHGCAP изменяет только состояние │ │ индикатора, не затрагивая состояния самой клавиши "CAPS" ! │ └──────────────────────────────────────────────────────────────────────┘ Отметим, что использование оператора CLEAR - это не единственный спо- соб выделения з а щ и щ е н н о й области в RAM. Может быть также "пере- двинута" Таблица PIT; тогда все машинные подпрограммы могут быть введены в область, начинающуюся с адреса &Н8000 . П р и м е р 2. Включение и выключение индикатора CAPS в соответствии ───────────── со значением аргумента функции USR . ┌───────────────────────────────────────────────────────────────────────┐ │ Для передачи о д н о б а й т н о г о аргумента подпрограмме вызовите│ │ подпрограмму, размещенную в ROM по адресу &H521F. Аргумент передается │ │ в регистр А. │ └───────────────────────────────────────────────────────────────────────┘ На языке ассемблера это пишется следующим образом: CD,1F,52 CALL 521F ; Вызов подпрограммы по адресу 521F C3,32,01 JP 0132 ; Перейти к адресу 0132 Приведем окончательную программу: 10 CLEAR 200,&HD000 'Резервирование области в RAM 20 AD=&HD000 30 READ A$:IF A$="Z" THEN 100 50 A=VAL("&H"+A$):POKE AD,A:AD=AD+1:GOTO 30 100 DEFUSR=&HD000:A=USR(0) 220 DATA CD,1F,52 :' CALL 521F 230 DATA C3,32,01 :' JP 0132 240 DATA "Z" Заметьте, что командa PRINT USR(0) в к л ю ч а е т индикатор, а кома- нда PRINT USR(1) в ы к л ю ч а е т его. ┌────────────────────────────────────────────────────────────────────────┐ │ Для передачи подпрограмме д в у х б а й т н о г о значения (например,│ │ адреса) используется подпрограмма, размещенная в ROM по адресу &H2F8A │ │ и использующая значение, хранящееся в шестнадцатибитном регистре HL. │ └────────────────────────────────────────────────────────────────────────┘ Приведем теперь список некоторых подпрограмм BIOS, использующих при ра- боте параметры. 1) запись числа (data) в регистр (register) видеопроцессора LD C,register LD B,data CALL 0047 RET 2) запись числа (data) в видеопамять (address) LD HL,address LD A,data CALL 004D RET 3) заполнение VRAM одним и тем же числом LD HL,address LD BC,length LD A,data CALL 0056 RET 4) копирование блока из VRAM в RAM LD HL,address VRAM LD DE,address MEMORY LD BC,length CALL 0059 RET 5) копирование блока из ROM или RAM в VRAM LD HL,address MEMORY LD DE,address VRAM LD BC,length CALL 005C RET 6) запись числа в звукогенератор LD A,register LD E,data CALL 0093 RET 7) вывод символа на принтер LD A,code ASCII CALL 00A5 RET 8) вывод символа на экран LD A,code ASCII CALL 00A2 RET 9) установка курсора в нужном месте экрана LD H,coordinate X LD L,coordinate Y CALL 00C6 RET 10) вывод символа на графический экран LD A,code ASCII CALL 008D RET П р и м е р 3. Запись в видеопамять. Изменение латинской буквы "A". ────────────── 10 CLEAR 200,&H9000:SCREEN 1 20 DEFUSR=&H9000:AD=&H9000 30 READ A$:IF A$="z" THEN 70 40 POKE AD,VAL("&h"+A$):AD=AD+1:GOTO 30 70 A=USR(0):END 90 DATA C3,0B,90 :' JP 900Bh ────┐ 100 DATA FF,81,81,81,81,81,81,FF:' Д а н н ы е │ 110 DATA 01,08,00 :' LD BC,08 ◀────┘ 120 DATA 11,08,02 :' LD DE,0208h 130 DATA 21,03,90 :' LD HL,9003h 150 DATA CD,5C,00 :' CALL 005Ch 190 DATA C9,"z" :' RET П р и м е р 4. Запись данных в регистр видеопроцессора (VDP). ────────────── 10 INPUT C,B:POKE &HA000,C:POKE &HA001,B 'C - регистр, B - значение 20 DEFUSR=&H9000:AD=&H9000 30 READ A$:IF A$="z" THEN 70 40 POKE AD,VAL("&h"+A$):AD=AD+1:GOTO 30 70 A=USR(0):END 90 DATA 3A,00,A0 :' LD A,(A000h) 100 DATA 4F :' LD C,A 110 DATA 3A,01,A0 :' LD A,(A001h) 120 DATA 47 :' LD B,A 130 DATA CD,47,00 :' CALL 0047h 140 DATA C9,"z" :' RET П р и м е р 5. Использование подпрограммы расширенного BIOS для по- ───────────── строения линии. 10 DATA 01,10,00 :' LD BC, начальная X-координата 11 DATA 11,10,00 :' LD DE, начальная Y-координата 12 DATA F7 :' RST #30 13 DATA 87 :' (для ученического компьютера - 83) 14 DATA 85,00 :' Адрес расширенного BIOS 15 DATA C9 :' RET 20 FOR I=0 TO 10:READ A$:POKE &HD000+I,VAL("&h"+A$):NEXTI 30 SCREEN 5 :' Возможны SCREEN 5 ÷ SCREEN 8 40 POKE &HFCB3,240 :' X-координата ─▶ GXPOS (FCB3,FCB4); 50 POKE &HFCB5,173 :' Y-координата ─▶ GYPOS (FCB5,FCB6); 60 POKE &HF3F2,15 :' Цвет ─▶ ATRBYT(F3F2); 70 DEFUSR=&HD000: Z=USR(0) :' Рисуем л и н и ю 80 A$=INPUT$(1) :' К о н е ц П р и м е р 6. ───────────── 5 'Автор программы: Беленький Г. (9 класс). 05.01.90 10 DATA 01,00,00 :' LD BC, X начальное 11 DATA 11,00,00 :' LD DE, ∗∗∗∗ ◀─────────────────┐ 12 DATA F7 :' RST #30 │ 13 DATA 87 :' (для ученического компьютера-83)│ 14 DATA 85,00 :' адрес расширенного BIOS │ 15 DATA C9 :' RET │ 20 FORI=0TO10:READA$:POKE &HD000+I,VAL("&h"+A$):NEXT:DEFUSR=&HD000:'│ 30 SCREEN 8 :' Возможны SCREEN 5 ÷ SCREEN 8 │ 40 POKE&HFCB3,255:POKE &HFCB4,0 :' X-координата фиксированой точки │ 50 POKE&HFCB5,0:POKE &HFCB5,0 :' Y-координата │ 60 FOR I=0 TO 211 :' │ 70 POKE &HF3F2,I :' Цвет │ 80 POKE &HD004,I :' Изменяющаяся Y-координата ──────┘ 90 Z=USR(0) :' Рисуем л и н и ю 100 NEXT I :' 110 A$=INPUT$(1) :' К о н е ц П р и м е р 7. Использование подпрограммы расширенного BIOS для по- ───────────── строения прямоугольника 10 DATA 01,10,00 :'LD BC, X-координата одного угла прямоугольника 11 DATA 11,10,00 :'LD DE, Y-координата 12 DATA F7 :'RST #30 13 DATA 87 :'(для ученического компьютера - 83) 14 DATA C9,00 :'Адрес расширенного BIOS 15 DATA C9 :'RET 20 FOR I=0 TO 10:READA$:POKE &HD000+I,VAL("&h"+A$):NEXTI:DEFUSR=&HD000 30 SCREEN 5 :' Возможны SCREEN 5 ÷ SCREEN 8 40 POKE &HFCB3,100 :' X-координата противоположного угла прямоуг. 50 POKE &HFCB5,100 :' Y-координата противоположного угла прямоуг. 60 POKE &HF3F2,15 :' Цвет 72 Z=USR(0) :' Рисуем п р я м о у г о л ь н и к 80 A$=INPUT$(1) :' К о н е ц В заключении пункта приведем более сложный пример. П р и м е р 8. "Утолщение" символов в режиме SCREEN 1. ───────────── 10 CLEAR 300,&HD000 20 AD=&HD000:DEFUSR=&HD00B 30 READ A$ 40 IF A$="RET" THEN A=USR(0):END 50 POKE AD,VAL("&h"+A$):AD=AD+1:GOTO 30 80 DATA 1E,00 :'D000 LD E,0 ; 90 DATA 3D :'D002 DEC A ; 100 DATA 1C :'D003 INC E ; 110 DATA BB :'D004 CP E ; 120 DATA C2,02,D0 :'D005 JP NZ,D002h ; 130 DATA C9 :'D008 RET ; 140 DATA 00,00 :'D009 NOP ; 150 DATA CD,6F,00 :'D00B CALL 006Fh ;Установка режима SCREEN 1 160 DATA 16,00 :'D00E LD D,0 ; 170 DATA 06,FF :'D010 LD B,FFh ;255 символов 180 DATA 21,00,00 :'D012 LD HL,0 ; 190 DATA 3E,08 :'D015 LD A,8 ; 200 DATA 32,3F,D0 :'D017 LD (D03Fh),A ; 210 DATA CD,4A,00 :'D01A CALL 004Ah ;Читаем видеопамять 220 DATA BA :'D01D CP D ; 230 DATA CA,2C,D0 :'D01E JP Z,D02Ch ; 240 DATA CB,87 :'D021 RES 0,A ; 250 DATA 4F :'D023 LD C,A ; 260 DATA CD,00,D0 :'D024 CALL D000h ; 270 DATA 79 :'D027 LD A,C ; 280 DATA B3 :'D028 OR E ; 290 DATA CD,4D,00 :'D029 CALL 004Dh ;Запись в видеопамять 300 DATA 23 :'D02C INC HL ; 310 DATA 3A,3F,D0 :'D02D LD A,(D03Fh) ; 320 DATA 5F :'D030 LD E,A ; 330 DATA 3E,00 :'D031 LD A,0 ; 340 DATA 1D :'D033 DEC E ; 350 DATA BB :'D034 CP E ; 360 DATA 7B :'D035 LD A,E ; 370 DATA 32,3F,D0 :'D036 LD (D03Fh),A ; 380 DATA C2,1A,D0 :'D039 JP NZ,D01Ah ; 390 DATA 10,D7 :'D03C DJNZ $-27h ;Переход по адресу D015h 400 DATA C9 :'D03E RET 410 DATA RET П р и м е ч а н и е. Для написания программ на машинном языке обычно требуется п р о г р а м м а - м о н и т о р (т.е. программа, позволяющая подключать подпрограммы, написанные на машинном языке). Если Вы не пользуетесь программой-монитором, то приходится вводить дан- ные в операторах DATA, как мы это только что проделали. Однако для длин- ных программ этот метод утомителен. При малейшей ошибке система часто "за- висает"; естественный выход при этом - нажать кнопку сброса "RESET",а это означает, что все должно быть начато заново. Перед проверкой таких подпрограмм убедитесь,что они записаны на дискету!