Nicron #33
07 мая 1997
  Бейсик  

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

(C) WLODEK BLACK

Здравствуйте, друзья. Я снова пришел грузить вас на праздники...
Поздравляю всех с Праздником Победы!
Продолжаем тему занятия...

    КОГДА КАЛЬКУЛЯТОР БЕЙСИКА
         ПРОТИВОПОКАЗАН

Встречаются  ситуации,  когда  вычисления,   причем   достаточно
сложные (по сравнению с элементарными действиями типа  "сложить"
или  "вычесть"),  проводить  с  помощью   калькулятора   Бейсика
нецелесообразно  или  же  невозможно  (например,  когда   нельзя
вызывать подпрограммы из ПЗУ).
Вот как можно распечатать на экране число в  десятичном  виде  в
диапазоне #0000-#FFFF:

Вход:	HL=число

DENARY	LD DE,10000 ; определяем, сколько раз по 10000
	CALL DIGIT
	LD DE,1000 ; -"- по 1000
	CALL DIGIT
	LD DE,100 ; -"- по 100
	CALL DIGIT
	LD DE,10 ; -"- по 10
	CALL DIGIT
	LD A,L ; остались единицы
	INC A ; добавляем единицу
	JR DIGIT2
DIGIT	XOR A ; обнуляем счетчик и одновр. сбрасываем флаг C
DIGIT1	SBC HL,DE ; вычитаем в цикле до появления заема
	INC A ; наращиваем счетчик
	JR NC,DIGIT1 ; цикл до появления заема
	ADD HL,DE ; восстанавливаем остаток
DIGIT2	ADD A,47 ; прибавляем 47 для получ-я кода символа цифры

	JP PRINT ; печатаем цифру

Алгоритм  процедуры  DENARY  прост:  последовательно  определяя,
сколько раз по 10000, по  1000  и  т.д.  содержится  в  исходном
числе,  подсчитываем  последовательно  цифры   всех   десятичных
разрядов числа и печатаем их. Команда INC A не  влияет  на  флаг
переноса, поэтому ее можно  использовать  после  SBC  HL,DE.  На
метке DIGIT2 стоит команда ADD A,47.  Почему  не  48,  ведь  код
символа цифры = значение цифры + 48 ? Дело в том, что счетчик на
регистре A всегда будет делать один лишний оборот, то есть в нем
на выходе из подпрограммы DIGIT всегда будет на единицу  больше,
чем "надо". Вот эту лишнюю единицу мы и компенсируем,  прибавляя
47, а не 48. А вот при печати разряда  единиц,  когда  мы  берем
число единиц прямо из  регистра  L,  не  используя  подпрограмму
DIGIT, лишней единички не возникает, и ее  мы  сами  прибавляем,
выполняя INC A.
Процедура печати PRINT не должна изменять HL.

А вот одно из решений противоположной задачи -  получения  числа
по его  записи  в  виде  цифр.  В  качестве  примера  я  приведу
процедуру "CALC"  из  программы  AUTOHISTORIZER-2.  В  "хистори"
сервера каждая строка выглядит аналогично вот этой:

4628902 <  NICRON32 B 214

В  22-24  позициях  (считая   слева   от   нуля)   располагается
трехзначное  число,  показывающее  длину   файла   в   секторах.
Автохисторизатор в числе прочих  имеет  функцию  подсчета  суммы
скачанных  секторов.  Вот  как  запись   числа   в   виде   цифр
преобразуется в значение числа:

Вход:  HL=предыдущее значение суммы;
Выход: HL=новое значение суммы:

CALC	PUSH HL
	LD IX,(TMPPOI) ; указатель на начало строки
	LD E,(IX+22) ; цифра сотен
	LD D,0
	LD HL,SUM100-48 ; список сотен со смещением
	ADD HL,DE
	LD E,(HL) ; значение сотен
	POP HL
	ADD HL,DE ; добавили сотни
	PUSH HL
	LD E,(IX+23) ; цифра десятков
	LD HL,SUM10-48 ; список десятков со смещением
	ADD HL,DE
	LD E,(HL) ; значение десятков
	POP HL
	ADD HL,DE ; добавили десятки
 	LD A,(IX+24) ; цифра единиц
	SUB 48
	LD E,A ; значение единиц
	ADD HL,DE ; добавили единицы
	RET ; готово
SUM100	DEFB 0,100,200
SUM10	DEFB 0,10,20,30,40,50,60,70,80,90

Алгоритм построен  на  использовании  списков  готовых  значений
сотен и десятков. По цифре в записи  числа  определяется  нужный
элемент списка, считывается значение из списка и прибавляется  к
общей сумме. Единицы определяются проще - путем вычитания 48  из
кода символа цифры. Нетрудно увидеть, что в процедуре  CALC  нет
циклов; время исполнения процедуры не зависит от значения числа.
Попробуйте предложить решение подобной задачи для случая,  когда
в символьной записи числа в разряде сотен может оказаться  цифра
больше 2 - в этом случае даже 300 уже не поместится в  байт.  Из
чего тогда должен состоять список значений сотен?
На  основе  прошлых  статей  вы   наверняка   сможете   написать
процедуру, аналогичную по назначению  "CALC",  с  использованием
функции  VAL  калькулятора  Бейсика.  Она   будет   короче,   не
сомневаюсь. Но вот по времени исполнения она окажется совершенно
не пригодной для обработки хистори, которая  может  состоять  из
сотен строк!
Автохисторизатор затрачивает на обработку  хистори  обьемом  6-8
килобайт (типичное значение для  одного  сеанса  сервера  RABBIT
BBS) всего около 4 секунд (не считая  еще  около  30  секунд  на
загрузку-выгрузку файлов с  диска),  выполняя,  помимо  подсчета
баланса  скачанных/закачанных  секторов,  еще  и   идентификацию
звонивших на сервер пользователей по списку ph_v.

P.S. Пользуясь случаем, хочу обратить внимание операторов BBS на
появление CDOS 1.14. 14-я версия не виснет при появлении в линии
длинного гудка  или  другого  непрерывного  тонального  сигнала,
пусть даже меняющегося по частоте.  Я  модернизировал  процедуру
приема, и теперь она безошибочно распознает посторонние сигналы.
Если у вас повисал  сервер,  когда  абонент  бросал  трубку  без
подачи команды END, или когда сервер поднимал трубку,  а  там  -
длинный  гудок,   попробуйте   воспользоваться   CDOS1.14.   При
обнаружении длинного  гудка  или  другого  постороннего  сигнала
трубка будет положена через 30  секунд.  При  наличии  модемного
сигнала    работа    происходит    обычным    образом.     Прошу
пользователей-не-сисопов не беспокоиться:  14-я версия  -  чисто
серверная;  для  пользовательской  работы  она   только   вносит
неудобства. Уважаемые сисопы, пожалуйста, скачивайте у  меня  (и
не только) 14-ю CDOS, пользуйтесь! Инструкция с более  подробным
описанием программы имеется на сервере.




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

Похожие статьи:
Pепopтaж o Сhaos Сonstructions`000 - от Сooler/Psycho (продолжение).
Soft Wars - разгром газеты Amiga.
Программистам - Перехват Reset'a на 128к машинах (на примере игры Doble Xinox).

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