[<>]
~~TOC wide~~
{{anchor:n101}}
====== Приложение 1. MSX BASIC и машинный язык ======
\\
Покажите, как операторы языка ассемблера можно
\\ чередовать с операторами данного языка.
—//Д.Уолш. Руководство по созданию документации
\\ для математического обеспечения//
{{anchor:n1011}}
===== 1.1. Связь программы на MSX BASIC с подпрограммами в машинных кодах =====
FIXME
Перед выполнением оператора [[msx:basic:|]] выполняется проверка его правильности,и в случае обнаружения ошибок на экран выводятся сообщения. Выполнимый оператор сначала преобразуется в последовательность команд,которые может "понять" процессор. Эти подпрограммы заранее помещены в ROM на этапе изготовления компьютера.
Интерпретация операторов [[msx:basic:|]] занимает определенное время; в некоторых случаях, однако, это время должно быть сокращено (например, для игровых программ). Это можно сделать двумя способами:
- основная программа пишется на [[msx:basic:|]], а в ситуациях, когда интерпретатор [[msx:basic:|]] не обеспечивает нужного быстродействия, используются подпрограммы, написанные в машинных кодах;
- вся программа пишется на машинном языке.
Отметим, что существует обширная литература по этому кругу проблем, и читатель, овладевший языком [[msx:basic:|]] и желающий научиться хорошо программировать на машинном языке, может обратиться к этой литературе [ [[bibliography#b45|[45]]], [[bibliography#b66|[66]]], [[bibliography#b67|[67]]] ].
Для обращения к подпрограммам в машинных кодах из программ на языке [[msx:basic:|]] используются оператор ''[[#DEFUSR]]'' и функция ''[[#USR]]''.
{{anchor:def_usr}}
Оператор ''DEFUSR'' определяет начальный адрес машинной подпрограммы.
Синтаксис оператора:
DEFUSR[n] = адрес
,
где:
* DEFUSR("DEFine USing Routine"-"определить используемую подпрограмму") — служебные слова;
* ''n'' — целое число,принадлежащее [0,9] и указывающее номер подпрограммы (по умолчанию значение n равно 0);
* ''адрес'' — арифметическое выражение, целая часть значения которого определяет начальный адрес подпрограммы.
Таким образом, в программе на языке [[msx:basic:|]] может использоваться до //десяти// подпрограмм в машинных кодах одновременно.Они различаются по номеру (от 0 до 9), который записываются вслед за служебным словом ''DEFUSR''.
Например, оператор ''DEFUSR1=&HA000'' указывает, что начальным адресом программы с номером 1 является &HA000 .
{{anchor:usr}}
Функция
USR[n](α)
,
где:
* ''USR'' ("USer"-"пользователь") — служебное слово;
* ''n'' — десятичная цифра (0,1,2,3,4,5,6,7,8,9), причем по умолчанию n=0;
* ''α'' — арифметическое или строковое выражение (которое, кстати,может отсутствовать), позволяет //выполнить// подпрограмму, написанную на машинном языке.
{{anchor:e10101-00}} __//Пример 1//__.
\\ Пусть, например, одна машинная подпрограмма начинается с адреса &HD000, а другая - с адреса &HD100 :
DEFUSR0=&HD000:DEFUSR1=&HD100
Теперь выполнить эти подпрограммы можно при помощи операторов:
A=USR0(A):A=USR1(A)
Значение выражения α передается подпрограмме, написанной в машинных кодах. При вызове подпрограммы число байтов,занимаемое этим значением, заносится в регистр A микропроцессора Z80 и в ячейку памяти с адресом &HF663 .
^ Содержимое регистра А ^ Тип значения выражения α ^
| 2 | Целочисленный |
| 4 | Вещественный с одинарной точностью |
| 8 | Вещественный с двойной точностью |
| 3 | Строковый |
Если аргументом функции ''USR'' является //нуль// , то в подпрограмму в машинных кодах ничего не передается!
Отметим, что значение, возвращаемое функцией ''USR'', представляет собой значение ее аргумента.
Если α — арифметическое выражение, то адрес его значения хранится в регистре HL .
Если же α — строковое выражение, то ссылка на адрес, по которому хранится значение α, содержится в регистре DE.
Ниже мы расскажем Вам и о других способах передачи аргументов машинным подпрограммам.
**Примеры**
\\
Некоторые люди учатся читая, другие - делая.
\\ Преуспевающие делают и то, и другое.
—//П.Браун//
\\
Примеры, немногочисленные, лаконичные, приведенные
\\ к месту, сообщают рассуждению блеск,глубину и
\\ убедительность, но оно становится невразумительным,
\\ если примеров и подробностей чересчур много...
—//Л.К.де Вовенарг. Размышления и максимы//
{{anchor:e10101-01}} __//Пример 1//__. Загрузка регистров A и BC константами.
\\ {{.examples:10101-01.bas|}} \\ [[+tab|wmsxbpge>10101-01.bas]]
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
{{anchor:e10101-02}} __//Пример 2//__. Пересылка содержимого регистра C в регистр A.
\\ {{.examples:10101-02.bas|}} \\ [[+tab|wmsxbpge>10101-02.bas]]
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
{{anchor:e10101-03}} __//Пример 3//__. Загрузка содержимого регистра A в память по адресу, указанному в регистре BC.
\\ {{.examples:10101-03.bas|}} \\ [[+tab|wmsxbpge>10101-03.bas]]
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
{{anchor:e10101-04}} __//Пример 4//__. Проиллюстрируем работу команды EX DE, HL.
\\ {{.examples:10101-04.bas|}} \\ [[+tab|wmsxbpge>10101-04.bas]]
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
{{anchor:e10101-05}} __//Пример 5//__. Сложение целых чисел, принадлежащих отрезку [0,255].
\\ {{.examples:10101-05.bas|}} \\ [[+tab|wmsxbpge>10101-05.bas]]
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 ; Возврат из подпрограммы
{{anchor:e10101-06}} __//Пример 6//__. Умножение содержимого двух ячеек памяти.
\\ {{.examples:10101-06.bas|}} \\ [[+tab|wmsxbpge>10101-06.bas]]
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
{{anchor:e10101-07}} __//Пример 7//__. Нахождение суммы целых чисел от 1 до 10.
\\ {{.examples:10101-07.bas|}} \\ [[+tab|wmsxbpge>10101-07.bas]]
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
{{anchor:e10101-08}} __//Пример 8//__. Нахождение большего из двух чисел.
\\ {{.examples:10101-08.bas|}} \\ [[+tab|wmsxbpge>10101-08.bas]]
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.
{{anchor:e10101-09}} __//Пример 9//__. Нахождение большего из двух чисел.
\\ {{.examples:10101-09.bas|}} \\ [[+tab|wmsxbpge>10101-09.bas]]
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(&HC000)
run
В регистре HL находится число: &hF7F6
Это есть адрес аргумента функции USR
Проверяем этот факт:... и вправду 6
Результат работы подпрограммы в машинных кодах: 12
Ok
{{anchor:e10101-10}} __//Пример 10//__. Нахождение большего из двух чисел.
\\ {{.examples:10101-10.bas|}} \\ [[+tab|wmsxbpge>10101-10.bas]]
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.
\\
Если вы не тот, кто наверху, значит,
\\ вы тот, кто внизу.
—//С.Поттер//
{{anchor:e10101-11}} __//Пример 11//__. Сортировка целочисленного массива , содержащего 5 элементов.
\\ {{.examples:10101-11.bas|}} \\ [[+tab|wmsxbpge>10101-11.bas]]
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 ◀──┘
{{anchor:e10101-12}} __//Пример 12//__. Вычисление факториала "маленьких" целых чисел
\\ {{.examples:10101-12.bas|}} \\ [[+tab|wmsxbpge>10101-12.bas]]
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
{{anchor:e10101-13}} __//Пример 13//__. Умножение шестнадцатиразрядных целых чисел без знака.
\\ {{.examples:10101-13.bas|}} \\ [[+tab|wmsxbpge>10101-13.bas]]
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
----
[<>]
{{tag>MSX msxbdpl}}