Программирование - калькулятор бейсика (продолжение).
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. Но все же
лучше не рисковать!
Другие статьи номера:
|
|
|
|
Программирование - калькулятор бейсика (продолжение).
|
|
|
|
|
|
|
|
|
|
|
|
|