Nicron #31
24 апреля 1997
  Бейсик  

Программирование - калькулятор бейсика (продолжение).

WLODEK BLACK

     КАЛЬКУЛЯТОР БЕЙСИКА

(Продолжение)

Работать с вещественными  числами  в  машинных  кодах,  конечно,
сложнее,  нежели  в  Бейсике,  но  не   настолько,   чтобы   это
становилось непонятным. При использовании калькулятора из  кодов
нужно  прежде  всего  четко  разделить  данные  на  константы  и
изменяющиеся по ходу дела данные  (не  хочу  употреблять  термин
"переменные").  Константы  имеет  смысл  просчитать  заранее   и
занести в тело программы в готовом виде - 5-байтовом  внутреннем
представлении.   Выяснить   значение   констант   в   5-байтовом
представлении легко прямо в Бейсике. Наберите такую строку:

10 PRINT 2.7182818

(Это число "e"). Посмотрев  содержимое  памяти  с  адреса  23872
(если    TR    DOS    инициализирована),     обнаружим     такую
последовательность кодов:


F5  32  2E  37  31  38  32  38  31  38  0E  82  2D  F8  54  3A

P    2   .   7   1   8   2   8   1   8
R
I
N
T


Маркер #0E разделяет ASCII-запись числа и внутренний  5-байтовый
формат. Списав куда-нибудь значения 5-и  байтов,  стоящих  после
#0E,  мы  запомним  значение   константы.   Данные   после   #0E
предназначены для регистров в следующем порядке:


0E  82  2D  F8  54  3A

     A   E   D   C   B


Занеся их в регистры процессора именно в таком порядке и  вызвав
затем  процедуру  STK-STORE  (#2AB2),  мы   получим   на   стеке
калькулятора значение желаемого  числа.  Понятно,  что,  написав
после "PRINT" любое  число  и  просмотрев  затем  память,  можно
узнать 5-байтовое представление любого вещественного значения  в
диапазоне, допускаемом Бейсиком.

Внимание  -  глюк!  И  извинения.  В предыдущем номере закралась
неточность. Вот правильный список процедур STK-STORE:

#2AB1 - занесение в стек параметров строки (A обнуляется);
#2AB2 - занесение в стек 5-байтового значения числа;
#2ABB - занесение в стек регистров A,E,D,C,B без контроля.

Существует  еще  так  называемая  "упакованная"   форма   числа,
требующая только 4 байта для  хранения  значения.  Прямо  скажу,
никогда не пытался ею пользоваться.  В  одной  из  умных  книжек
упомянут "глюк" упакованной формы: 0.5 <> 1/2. Ну да ладно...
Если при вычислениях или на стадии ввода-вывода будет обнаружена
какая-либо некорректность, Бейсик выполнит переход через RST #08
и стек возврата по ошибке. Иными словами, если  система  Бейсика
не  была  модифицирована  (извращена),  любая  традиционная  для
Бейсика ошибка приведет  к  "вылету"  программы  пользователя  в
обычный режим ввода команд Бейсика с сообщением об ошибке в низу
экрана.   Если   подобное   недопустимо,    нужно    обязательно
переорганизовать  стек  возврата  по  ошибке  (ERR_SP).   Честно
говоря, не знаю, надо ли сейчас рассказывать, как это  делается.
Давайте поступим так: вы напишете мне  или  на  адрес  редакции,
если такой рассказ необходим, и он появится.

Если по ходу  выполнения  машиннокодовой  программы  потребуется
ввести числовые данные извне, то по крайней  мере  с  клавиатуры
это разумнее всего сделать в виде обычных символов цифр, как  мы
вводим данные на запрос по INPUT. Имитировать INPUT в  кодах  не
стоит;  лучше  обеспечить  ввод  символьной  строки   каким-либо
известным  вам  способом,  а  дальше   преобразовать   введенную
последовательность    символов    во    внутреннее    5-байтовое
представление числа. Итак, у нас  есть:  символьное  изображение
числа; нужно получить:  внутреннее  представление  числа.  Очень
легко это делается  путем  вычисления  выражения  VAL  "строка".
Параметры введенной строки (адрес начала и длина)  заносятся  на
стек калькулятора, а затем выполняется функция VAL:

	LD	DE,ASC_A ; адрес начала строки
	LD	BC,ASC_B-ASC_A ; длина строки
	CALL	#2AB1 ; STK-STORE для параметров строки
	LD	B,#1D ; код функции VAL
	RST	#28 ; вызов калькулятора
	DEFB	#3B ; код функции fp-calc-2

; на этом месте в стеке получено внутреннее представление числа

	DEFB	....... ; коды других операций калькулятора

	DEFB	#38 ; выход из калькулятора
	...........
ASC_A	DEFM "2.7182818" ; строка символов, изображающая число
ASC_B

Функция VAL при своей работе вызывает калькулятор рекурсивно для
выполнения многочисленных пересчетов значений ASC-кодов и других
промежуточных значений. Из-за этого ее нельзя включать  в  общий
список DEFB кодов операций калькулятора, так как в таком  случае
теряются  другие  данные,  и  происходит   какая-нибудь   ошибка
Бейсика.   Для   вызова   рекурсивных   функций    предусмотрена
специальная операция "fp-calc-2" - вторичный вызов калькулятора.
При использовании fp-calc-2 код операции помещается в регистр B,
а в теле DEFB ставится код #3B - обращение  к  fp-calc-2.  После
#3B могут располагаться коды последующих операций  калькулятора.
Так же нужно обращаться ко всем функциям, вычисляющим выражения,
в  том  числе  строковые - VAL$.  Строка символов,  определяющая
число, может и не быть изображением собственно числа - в  строке
можно  записать  любое  выражение,  даже  содержащее  переменные
Бейсика, и оно будет вычислено! Не  трудно  представить,  что  в
такой работе задействован и интерпретатор Бейсика. Если  функция
VAL (или VAL$) вызывается только для  преобразования  введенного
выражения, а затем сразу же производится выход  из  калькулятора
(скажем, для подготовки и ввода других данных), обращение к  VAL
можно подсократить, вызывая ее не через  fp-calc-2,  а  напрямую
через CALL  по  известному  адресу:  ...облом!!!  В  справочнике
очепятка... Адрес VAL$ - #35DE, а VAL - ? Ну да ладно, VAL$, так
VAL$. Покажем использование  VAL$.  Вот  фрагмент  из  некоторых
мелких утилит, которыми я пользуюсь  для  оформления  серверного
диска, обработки "history" и тому подобных забот сисопа:

Бейсик:

INPUT "Source file:";a$:INPUT "Destination file:";b$:RANDOMIZE
USR ....

Коды:

	LD	DE,VARLIN ; адрес строки "а$"
	LD	BC,2 ; длина строки
	CALL	#2AB1 ; STK-STORE
	CALL	#35DE ; VAL$
	CALL	#2BF1 ; STK-FETCH
; на этом месте в DE - адрес значения символьной переменной a$,
; в BC - длина переменной а$
	LD	A,C ; проверяем, не больше 8 символов ли?
	CP	9
	JR	C,VL8 ; если не больше 8 символов
	LD	C,8 ; иначе берем только первые 8 символов
VL8	LD	B,0 ; это на всякий случай - вдруг кто-нибудь
;			264 символа введет? :-)
	LD	HL,FILNAM ; адрес буфера имени файла
	EX	DE,HL
	LDIR ; имя файла из переменной a$ переслано в буфер

	.............

VARLIN	DEFM "A$" ; "изображение" символьного выражения,
			состоящего только из одной переменной a$
	.............
FILNAM	DEFM "        " ; буфер имени файла

Вот  так,  без  особого  труда,   а,   главное,   очень   быстро
производится ввод имени файла в некий буфер, из  которого  потом
имя файла используется в соответствии с алгоритмом программы.
CALL #35DE заменяет цепочку:

	LD	B,#18 ; код операции VAL$
	RST	#28
	DEFB	#3B,#38					,

то есть 3 байта вместо 5 плюс увеличение скорости исполнения.
Конечно, если  после  DEFB  #3B  следуют  другие  коды  операций
калькулятора, разумнее вызывать VAL$ через fp-calc-2.

Через VAL и VAL$ легко вычисляются всевозможные полезные функции
типа RND, INKEY$ и т.д.:
RND = VAL CHR$ 165;
INKEY$ = VAL$ CHR$ 166.
Причем  VAL  и  VAL$  от  одиночного  простого  значения  (не от
выражения)  можно  вычислять  напрямую  без fp-calc-2. Но все же
лучше не рисковать!




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

Похожие статьи:
Знаете ли вы... - Гасить ВГ-шку нулем в порт #FF нельзя. Перед записью через #3D13 на конкретную дорожку, если моторчик включен, а головка на ней торчит. Как ускорить процедуру чтения и загрузки через #3D13, если необходимо загрузить/записать несколько небольших блоков подряд.
Смайлик - Молоко вдвойне смешней, если после огурцов ...
Coding - Как кодить оптимально: генерация синус-таблицы, вывод атрибутного черно/белого спрайта.

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