(c) Колотов Сергей, г.Шадринск,
SerzhSoft, V, 1997.
ПЕЧАТЬ ЧИСЕЛ В РАЗЛИЧНЫХ
СИСТЕМАХ СЧИСЛЕНИЯ
Одной из важнейших компьютер-
ных задач является вывод чисел
на экран (печать). Причем числа
могут представляться в различных
системах счисления, которых су-
ществует не так уж и мало.
Наиболее привычной для всех
нас, конечно же, является деся-
тичная система, т.е. система
счисления с основанием 10. Де-
сятка здесь фигурирует не слу-
чайно: человек с глубокой древ-
ности считал с помощью пальцев
рук, а их, как известно, - де-
сять. Но компьютеру "привычней"
считать не в десятичной системе,
а в двоичной, т.к. вся информа-
ция в нем представляется элек-
трическими зарядами: есть ток
(1) и нет тока (0). Так как же
перевести с "компьютерного" язы-
ка на нормальный, человеческий?
Следующая процедура выпол-
няет печать числа, заданного в
регистре A (аккумуляторе) в де-
сятичной форме. Печать выпол-
няется стандартной командой RST
#10 в текущий поток вывода. Дли-
на процедуры 23 байта. В своей
работе она использует 3-байтную
таблицу степеней десятки и, при
выполнении, в цикле подсчитыва-
ет и распечатывает количество
сотен (первая цифра), десятков
(вторая) и единиц (третья цифра)
числа.
см. Listing 1.
Но все же маловероятно, что
так и не появится необходимость
печати, скажем, в шестнадцате-
ричной или двоичной системах
счисления. Да хотя бы и в той же
десятичной, но, например, чисел
в диапазоне не от 0 до 255, а от
0 до 65535. Поэтому достаточно
целесообразно всегда "иметь под
рукой" библиотечку процедур,
предназначенных для печати чи-
сел в самых различных системах
счисления, в самых различных
диапазонах. Что мной и реализо-
вано...
см. Listing 2.
В состав данной библиотеки
входят процедуры для печати чи-
сел в самых различных системах
счисления. А именно: десятичная,
шестнадцатеричная, двоичная, ус-
тановленная пользователем (на-
пример, восьмеричная) и, как
особо экзотичная, система рим-
ских чисел.
Процедуры, составляющие биб-
лиотеку, имеют много общего, и,
полностью разобравшись с одной,
можно более быстро вникнуть в
принцип работы остальных. Все
заголовки процедур печати начи-
наются с символа P (Print). Да-
лее идут три буквы, определяю-
щие систему, в которой распеча-
тывается число. Затем, отделяясь
символом подчеркивания, следует
один из символов B или W, опре-
деляя диапазон печатаемого чис-
ла: байт (Byte) или слово
(Word). В первом случае (B) пе-
ред вызовом процедуры число дол-
жно находиться в регистре A (ак-
кумуляторе), а во втором случае
(W) - в регистровой паре HL. Пе-
чать производится в текущий по-
ток вывода (команда RST #10).
Описание библиотеки PDES LIB
1. Печать десятичных чисел
1) Процедура PDEC_W - печать
числа, находящегося в ре-
гистровой паре HL. Диапа-
зон печати: 00000..65535.
Длина процедуры 26 байт.
Использует таблицу степе-
ней десятки DECTB_W.
2) Процедура PDEC_B - печать
числа в A (000..255), 10
байт. В своей работе вызы-
вает PDEC_W.
3) Таблица DECTB_W - содержит
5 степеней десятки, задан-
ных как слова (2 байта) -
длина 10 байт.
Общая длина раздела: 10+10+26
=46 байт.
2. Печать шестнадцатеричных
чисел
1) Пр-ра PHEX_LB - печать мл.
полубайта в A (0..F), 14
байт.
2) Пр-ра PHEX_HB - печать ст.
полубайта в A (0..F), 4
байта. Меняет местами по-
лубайты в A и вызывает
PHEX_LB.
3) Пр-ра PHEX_B - печать чис-
ла в A (00..FF), 3 байта.
Два раза вызывает PHEX_HB.
4) Пр-ра PHEX_W - печать чис-
ла в HL (0000..FFFF), 5
байт. Два раза вызывает
PHEX_B.
Общая длина раздела: 5+3+4+14
=26 байт.
3. Печать двоичных чисел
1) Пр-ра PBIN_B - печать чис-
ла в A (8 разрядов), 12
байт.
2) Пр-ра PBIN_W - печать чис-
ла в HL (16 разрядов), 5
байт. Два раза вызывает
PBIN_B.
Общая длина раздела: 5+12=17
байт.
4. Печать чисел в системе счис-
ления с основанием, установлен-
ным пользователем.
Прототипом послужили проце-
дуры, опубликованные в отличном
справочнике - книге Н.Родионова
и А.Ларченко "ZX-Spectrum для
пользователей и программистов".
1) Пр-ра MKUSETB - заполнение
таблицы степеней USE_TBL с
основанием, заданным в ак-
кумуляторе. Длина пр-ры 28
байт.
2) Пр-ра PUSE_W - печать чис-
ла в HL. Использует ранее
созданную MKUSETB таблицу
степеней USE_TBL. 33 бай-
та.
3) Таблица USE_TBL - таблица
степеней, 32 байта.
Общая длина раздела: 32+33+28
=93 байта.
5. Печать римских чисел
1) Пр-ра PRIM_W - печать чис-
ла в HL (I..MMMCMXCIX), 77
байт. Использует таблицу
RIM_TBL.
2) Пр-ра PRIM_B - печать чис-
ла в A (I..CCLV). Длина 3
байта. Вызывает процедуру
PRIM_W.
3) Таблица RIM_TBL - таблица
значимости букв в написа-
нии римских чисел. Длина:
7*3=21 байт.
Общая длина раздела: 21+3+77
=101 байт.
Полный размер библиотеки: 283
байта.
140.
Listing 1. Процедура печати байта в десятичной системе.
PDEC_B ;Печать десятичного числа в аккумуляторе3 (000..255)
LD HL,DECTB_B ;адрес таблицы степеней десятки
LD B,#03 ;макс. возможное количество цифр
LP_PDB1 LD C,"0"-1 ;инициализация счетчика
LP_PDB2 INC C ;увеличиваем счетчик
SUB (HL) ;вычитаем текущую степень десятки
JR NC,LP_PDB2 ;повторять, пока A>=0
ADD A,(HL) ;A=A mod (HL); C=STR$(A div (HL))
PUSH AF ;сохранить A на стеке
LD A,C ;текущая цифра
RST #10 ;печать цифры
POP AF ;восстановление аккумулятора
INC HL ;переход к след. степени десятки
DJNZ LP_PDB1 ;закрутили цикл по цифрам
RET ;выход из процедуры
;
DECTB_B DB 100,10,1 ;Таблица степеней3 10-ки для byte
Listing 2. Библиотечка печати в различных системах счисления.
;------ PDES LIB ------
;Printing in Different Evaluation Systems LIBrary
;(c) SerzhSoft, Shadrinsk, V,3 1997
;
_NULL EQU 0 ;используется в изменяемых командах
;
;--- print in DEC system ---
;
PDEC_B ;Печать десятичного числа в A3 (000..255)
LD L,A ;поместили
LD H,#00 ; число в HL
LD DE,DECTB_W+4 ;адрес в таблице, начиная с сотни
LD B,#03 ;макс. возможное кол-во цифр - три
JR LP_PDW1 ;переход на цикл печати
;
PDEC_W ;Печать десятичного числа в HL3 (00000..65535)
PUSH HL ;закинули печатаемое число на стек
LD HL,DECTB_W ;адрес таблицы степеней десятки
LD B,#05 ;макс. возможное количество цифр
LP_PDW1 LD E,(HL) ;взяли текущую степень
INC HL ; десятки из таблицы
LD D,(HL) ; и поместили в DE
INC HL ;перешли к след. элементу таблицы
EX (SP),HL ;адрес эл-та <-> печатаемое число
XOR A ;обнулили счетчик и флаг C для SBC
LP_PDW2 INC A ;увеличиваем счетчик
SBC HL,DE ;вычитаем текущую степень десятки
JR NC,LP_PDW2 ;повторяем пока HL>=0
ADD HL,DE ;HL=HL mod DE; A=HL div DE
ADD A,"0"-1 ;перевод A в ASCII-код ("0".."9")
RST #10 ;печать десятичной цифры
EX (SP),HL ;HL=адрес эл-та, число -> на стек
DJNZ LP_PDW1 ;цикл по цифрам
POP HL ;убрали оставшийся ноль со стека
RET ;выход из процедуры
;
DECTB_W DW 10000,1000,100,10,1 ;Таблица степеней десятки
;
;--- print in HEX system ---
;
PHEX_W ;Печать шестнадцатеричного числа в HL3 (0000..FFFF)
LD A,H ;печать старшего байта числа
CALL PHEX_B ;вызов процедуры HEX-печати байта
LD A,L ;печать младшего байта числа
;
PHEX_B ;Печать шестнадцатеричного числа в A3 (00..FF)
CALL PHEX_HB ;печать по полубайтам
;
PHEX_HB ;Печать старшего полубайта в A3 (0..F)
RRCA ;меняем
RRCA ; местами
RRCA ; старший
RRCA ; и младший полубайты
;
PHEX_LB ;Печать младшего полубайта в A3 (0..F)
PUSH AF ;сохранили A на стеке
AND #0F ;отбросили лишние старшие биты
CP #0A ;если A<10,
JR C,GO_PHL ; то перепрыгнули
ADD A,"A"-"9"-1 ;корректировка: после "9" идет "A"
GO_PHL ADD A,"0" ;перевод в ASCII-код
RST #10 ;печать HEX-цифры ("0".."F")
POP AF ;восстановили A со стека
RET ;выход из процедуры
;
;--- print in BIN system ---
;
PBIN_W ;Печать двоичного числа в HL (16 разрядов 0/1)
LD A,H ;печать старшего байта числа
CALL PBIN_B ;вызов процедуры BIN-печати байта
LD A,L ;печать младшего байта числа
;
PBIN_B ;Печать двоичного числа в A (8 разрядов 0/1)
LD B,#08 ;в байте - 8 битов
LP_PBB RLCA ;поочередно 'выдвигаем' биты в CF
PUSH AF ;сохранить A на стеке
LD A,#18 ;в зависимости от флага C:
RLA ; A="0" или A="1"
RST #10 ;печать очередного бита
POP AF ;восстановить A со стека
DJNZ LP_PBB ;поразрядный цикл
RET ;выход из процедуры
;
;--- print in USER system ---
;
PUSE_W ;Печать числа в установленной MKUSETB системе сч-я в HL
PUSH HL ;закинули печатаемое число на стек
HL_PUW LD HL,_NULL ;адрес конца таблицы степеней
LP_PUW1 DEC HL ;загружаем в DE очередную
LD D,(HL) ; степень числа
DEC HL ; из таблицы,
LD E,(HL) ; двигаясь 'сверху-вниз'
EX (SP),HL ;адрес таблицы <-> печат-е число
XOR A ;обнуление A и сброс флага C
LP_PUW2 INC A ;вычисляем очередной
SBC HL,DE ; разряд числа и результат
JR NC,LP_PUW2 ; помещаем в A (A=1+(HL div DE))
ADD HL,DE ;восстанавливаем (+) значение
ADD A,"0"-1 ;переводим A в ASCII-код
CP "9"+1 ;если код меньше "9",
JR C,GO_PUW1 ; то переход на печать
ADD A,"A"-"9"-1 ;коррекция (после "9" идет "A")
GO_PUW1 RST #10 ;печать очередной цифры числа
EX (SP),HL ;HL=адрес таблицы, число -> стек
DEC DE ;если степень
LD A,D ; числа не равна единице
OR E ; (самый первый элемент таблицы),
JR NZ,LP_PUW1 ; то продолжаем работу
POP HL ;убираем со стека ненужный 0
RET ;выход из процедуры
;
MKUSETB ;Создание таблицы степеней с основанием в A
LD HL,USE_TBL ;адрес создаваемой таблицы
LD DE,#0001 ;инициализация счетчика степени
LP_MUT1 LD (HL),E ;запись текущего
INC HL ; значения степени
LD (HL),D ; в таблицу и переход
INC HL ; к ее следующей ячейке
PUSH HL ;сохраняем адрес на стеке
LD B,A ;осн-е степени -> в счетчик цикла
LD HL,#0000 ;обнуление результата
LP_MUT2 ADD HL,DE ;подсчитываем результат
JR C,GO_MUT ;если HL>65535, то прерываем счет
DJNZ LP_MUT2 ;повторяем <основание> раз
EX DE,HL ;результат -> в DE (новая степень)
GO_MUT POP HL ;восстанавливаем адрес таблицы
JR NC,LP_MUT1 ;если не прерывались, то повторяем
LD (HL_PUW+1),HL ;адрес конца таблицы -> в PUSE_W
RET ;выход из процедуры
;
USE_TBL DS 32 ;Таблица степеней текущей системы счисления
;
;--- print in RIM system ---
;
PRIM_B ;Печать числа в римской записи в A (I..CCLV)
LD L,A ;скопировать
LD H,#00 ; A в HL
;
PRIM_W ;Печать числа в римской записи в HL (I..MMMCMXCIX)
PUSH HL ;закинули печатаемое число на стек
LD HL,RIM_TBL ;адрес таблицы знаков
DB #DD ;работаем с половинкой регистра IX
LD L,#07 ;LD XL,число знаков в р. счислении
LP_PRW1 LD E,(HL) ;считываем из таблицы значение
INC HL ; очередного знака римской системы
LD D,(HL) ; счисления и помещаем в DE
INC HL ;затем в регистр C считываем
LD C,(HL) ; ASCII-код знака
INC HL ;переход к следующему знаку
LD (HL_PRW+1),HL ;сохранили адрес следующего знака
EX (SP),HL ;адрес -> на стек, HL=печат. число
XOR A ;обнуление счетчика и сброс CF
LP_PRW2 INC A ;в цикле производим
SBC HL,DE ; деление HL на DE (вычитаем,
JR NC,LP_PRW2 ; пока нет переполнения)
ADD HL,DE ;восстанавливаем (+) значение
DEC A ;т.к. A на 1 больше HL div DE,
JR Z,GO_PRW1 ; то если A=1 - ничего не печатаем
LD B,A ;количество печатаемых символов
LP_PRW3 LD A,C ;код знака
RST #10 ;печатаем его
DJNZ LP_PRW3 ;сколько надо - столько и печатаем
GO_PRW1 DB #DD ;работаем с половинкой регистра IX
LD A,L ;LD A,XL - номер текущего знака
DEC A ;если это последний знак (I), то
JR Z,GO_PRW4 ; двухбуквенного сочетания нет
EX (SP),HL ;HL=адрес след. знака, число->стек
RRA ;если номер текущего знака четный,
JR C,GO_PRW2 ; то сочетание с следующим знаком
INC HL ;иначе - перепрыгнуть через один
INC HL ; знак. В результате получим адрес
INC HL ; знака, для получения двойного
GO_PRW2 LD A,C ; знакосочетания (IV,CM,XL...)
LD C,(HL) ;взяли из таблицы значение для
INC HL ; этого знака и получили
LD B,(HL) ; разность между значением
INC HL ; основного и этим значением.
EX DE,HL ; Например, для основного знака X
AND A ; дополнительным будет I, а их
SBC HL,BC ; разность соответственно:
140.
EX DE,HL ; 10-1=9, т.е. IX
LD C,A ;код основного знака
LD A,(HL) ;код дополнительного знака
EX (SP),HL ;HL=печатаемое число
SBC HL,DE ;если оно < дополнительного
JR C,GO_PRW3 ; значения, то не печатаем
RST #10 ;печать дополнительного знака
LD A,C ;основной знак
RST #10 ;печать
JR GO_PRW4 ;результат - двухбукв. сочетание
GO_PRW3 ADD HL,DE ;восстановили (+) у числа
GO_PRW4 EX (SP),HL ;число -> на стек
HL_PRW LD HL,_NULL ;адрес следующего знака в таблице
DB #DD ;работаем с половинкой регистра IX
DEC L ;DEC XL - уменьшаем счетчик знаков
JR NZ,LP_PRW1 ;крутим цикл, пока есть еще знаки
POP HL ;сняли ненужный уже ноль со стека
RET ;выход из процедуры
;
RIM_TBL ;Таблица значимости букв в написании римских чисел
DW 1000
DB "M" ;M=1000 ;CM=900
DW 500
DB "D" ;D=500 ;CD=400
DW 100
DB "C" ;C=100 ;XC=90
DW 50
DB "L" ;L=50 ;XL=40
DW 10
DB "X" ;X=10 ;IX=9
DW 5
DB "V" ;V=5 ;IV=4
DW 1
DB "I" ;I=1 ;-nop-
;
;--- end of PDES LIB ---
2
* * *