Страница Ивана Рощина > Статьи >

© Иван Рощин, Москва

ZXNet: 500:95/462.53
E-mail: bestview@mtu-net.ru
WWW: http://www.ivr.da.ru

Программа-калькулятор для работы с BIN, DEC, HEX-числами

Радиомир. Ваш компьютер» 1/2002)
Дата последнего редактирования: 22.02.2003.


Введение
Инструкция пользователя
Листинг программы
А как она работает?


Скачать программу в формате hobeta (2 КБ ZIP)

Введение

Иногда требуется провести вычисления, в которых участвуют не только десятичные числа, но и двоичные или шестнадцатеричные, или просто нужно перевести число из одной системы счисления в другую. Как это сделать?

В Бейсике можно работать только с десятичными числами и с целыми двоичными числами в диапазоне 0—65535 (с помощью функции BIN). В ассемблере ZX ASM (и в некоторых других ассемблерах) имеется встроенный калькулятор, поддерживающий все три системы счисления, но он может работать лишь с целыми числами в диапазоне 0—65535. А что делать, если числа дробные или выходят за пределы этого диапазона?

Пришлось написать свою программу-калькулятор, обладающую следующими возможностями:

Инструкция пользователя

После запуска вводите вычисляемое выражение точно так же, как если бы вы вычисляли его в Бейсике. При записи чисел можно использовать все три системы счисления. Двоичные числа предваряются символом «%», шестнадцатеричные — символом «#». Буквы A—F, используемые при записи шестнадцатеричных чисел, могут быть как прописными, так и строчными. Названия используемых функций, а также название константы PI необходимо набирать обязательно токенами, а не по буквам.


Примеры:

>>> (8.15–%1101.0111)/#ab5.07
    –.0019290213

>>> %.1011^2*SIN(#3c/180*PI)
    0.40933232

(здесь и далее «>>>» перед строкой означает, что эта строка вводится пользователем).


По умолчанию результат печатается в десятичном виде, но формат вывода можно изменить. Для этого перед вычисляемым выражением надо поместить специальную команду, состоящую из символа «=», после которого следуют символы «#», «%» или «d». Символ «#» указывает, что результат должен быть напечатан в шестнадцатеричном виде, «%» — в двоичном, а «d» (или «D», регистр не важен) — в десятичном. Команда отделяется от вычисляемого выражения пробелом.

Можно указать после «=» сразу несколько соответствующих символов (в любом порядке), чтобы результат печатался сразу в нескольких системах счисления.


Примеры:

>>> =% 16/2.5
    %1.10011001100110011001100110011*2^2

>>> =%#d 17*#.5E
    %1.100011111*2^2
    6.2421875
    #6.3E

Так как вычисляемое выражение в частном случае может быть просто числом, с помощью этой команды можно преобразовывать числа из одной системы счисления в другую.


Примеры:

>>> =# PI
    #3.243F6A88

>>> =%d #4C5A
    %100110001011010
    19546

Действие команды «=» относится лишь к текущему вычисляемому выражению. Есть другая, похожая на неё команда — «==», с такими же параметрами. Она устанавливает режим вывода «по умолчанию». Этот режим используется при печати результатов вычисления всех последующих выражений (кроме тех, в которых командой «=» явно задан другой формат вывода). Например, если вам надо, чтобы результат всегда печатался в шестнадцатеричном виде, просто подайте команду «==#», и тогда уже не нужно будет перед каждым выражением ставить команду «=#». Команда «==» может быть подана как перед выражением, так и сама по себе.


Примеры:

>>> ==%

>>> ==%# 1+2
    %11
    #3

Последний вычисленный результат помещается в переменную L, которую можно использовать при записи следующего выражения. Так как регистр букв в именах переменных Бейсика не различается, можно писать как «L», так и «l».


Пример:

>>> 13+#A
    23
>>> L+10
    33

Также вам доступны девять ячеек памяти, обозначаемых M(1)—M(9). В них можно сохранять результаты вычислений, чтобы использовать их в последующих вычислениях. Чтобы поместить значение в ячейку, нужно записать «M(x)=» (где x — номер ячейки) перед вычисляемым выражением (и после команд «=», «==», если они есть). M(1)—M(9) также являются переменными Бейсика, так что буква «M» в их записи может быть как прописной, так и строчной.


Пример:

>>> м(1)=4
    4
>>> =# м(2)=3*м(1)
    #C
>>> м(2)+3
    15

Вы можете определять свои функции и использовать их при вычислениях. Пусть, например, вам нужно вычислить значение функции f(x,y)=2x2–4xy+y2 в нескольких точках. После загрузки программы добавьте в неё такую строку:

    5 DEF FN F(X,Y)=2*X*X–4*X*Y+Y*Y

Запустив программу, вы можете использовать эту функцию при вычислениях точно так же, как и любую стандартную функцию Бейсика. Перед именем функции указывается «FN» — признак того, что она определена пользователем. Это ключевое слово необходимо набирать токеном, а не по буквам.


Пример:

>>> FN F(3,4)
    –14
>>> =# FN F(%1101.0011,–12)
    #4.64D2*16^2

Всего можно определить 26 различных функций (с именами от A до Z), в каждой из которых может быть 26 параметров (также от A до Z). Строки с определениями функций могут находиться в любом месте программы, не обязательно в начале.

Если понадобилось определить функцию, когда программа уже запущена, то можно намеренно допустить ошибку при записи выражения (например, просто введя строку, состоящую из единственного пробела), после чего выполнение программы прервётся, и вы сможете добавить строку с определением нужной функции.


Чтобы очистить экран от предыдущих вычислений, просто нажмите «Enter» вместо ввода выражения.

С помощью команды «Q» (или «q») можно выйти в TR-DOS. Вернуться обратно можно по команде «RETURN».


Теперь расскажу об обработке ошибочных ситуаций. Ошибки, связанные с неправильным указанием параметров в командах «=» и «==», а также ошибки при записи двоичных или шестнадцатеричных чисел обрабатываются в программе. При возникновении такой ошибки раздается звуковой сигнал и выводится сообщение «Error in expression».

Остальные ошибки обрабатываются непосредственно интерпретатором Бейсика. При их возникновении выполнение программы останавливается с выводом соответствующего сообщения («Number too big», «Invalid argument» и т.п.). После этого для продолжения работы с программой её надо перезапустить командой «RUN».


ВНИМАНИЕ! Из-за ошибки в ПЗУ Бейсика, если при вычислении выражения надо будет вычислить, чему равно –65535–1, результат окажется неправильным!

Листинг программы

    10 LET L=0: LET AdrL=( PEEK 23627)+( PEEK 23628)*256+1:
       REM L is first variable in program!
    20 DIM R(5): DIM M(9)
    30 LET AutoBin=0: LET AutoDec=1: LET AutoHex=0
    40 REM -= CLS, PRINT HEADER =-
    50 BORDER 0: INK 6: PAPER 0: CLS
    60 PRINT AT 0,5;"Bin,Dec,Hex Calculator": PRINT AT 1,2;"by
       Ivan Roshin, Moscow, 2001": PRINT
    70 REM -----= MAIN LOOP =-----
    80 INPUT a$
    90 IF a$="" THEN GO TO 40
   100 IF (a$ <> "Q" AND a$ <> "q") THEN GO TO 130
   110 CLS : INK 7: PRINT "Press <RETURN> to continue...":
       RANDOMIZE USR 15616
   120 GO TO 40
   130 INK 5: PRINT a$
   140 IF a$(1) <> "=" THEN GO TO 350
   150 LET a$=a$(2 TO LEN (a$))
   160 IF a$="" THEN GO TO 1420
   170 IF a$(1)="=" THEN LET a$=a$(2 TO LEN (a$)): GO TO 270
   180 REM ----= COMMAND "=" =----
   190 LET OutBin=0: LET OutDec=0: LET OutHex=0
   200 IF a$="" THEN GO TO 1420
   210 LET c$=a$(1): LET a$=a$(2 TO LEN (a$))
   220 IF c$="%" THEN LET OutBin=1: GO TO 200
   230 IF (c$="D" OR c$="d") THEN LET OutDec=1: GO TO 200
   240 IF c$="#" THEN LET OutHex=1: GO TO 200
   250 IF c$=" " THEN GO TO 370
   260 GO TO 1420
   270 REM ---= COMMAND "==" =---
   280 LET AutoBin=0: LET AutoDec=0: LET AutoHex=0
   290 IF a$="" THEN GO TO 70
   300 LET c$=a$(1): LET a$=a$(2 TO LEN (a$))
   310 IF c$="%" THEN LET AutoBin=1: GO TO 290
   320 IF (c$="D" OR c$="d") THEN LET AutoDec=1: GO TO 290
   330 IF c$="#" THEN LET AutoHex=1: GO TO 290
   340 IF c$ <> " " THEN GO TO 1420
   350 REM -= SET OUTPUT FORMAT =-
   360 LET OutBin=AutoBin: LET OutDec=AutoDec: LET
       OutHex=AutoHex
   370 REM ------= Memory =------
   380 LET Memory=0
   390 IF (a$(1) <> "M" AND a$(1) <> "m") THEN GO TO 420
   400 IF LEN (a$)<6 THEN GO TO 420
   410 IF (a$(2)="(" AND a$(4)=")" AND a$(5)="=") THEN LET
       Memory= VAL (a$(3)): LET a$=a$(6 TO LEN (a$))
   420 REM --= BIN,HEX -> DEC =--
   430 LET Begin=1
   440 IF LEN (a$)<Begin THEN GO TO 660
   450 IF a$(Begin)="%" THEN LET N=2: GO TO 480
   460 IF a$(Begin)="#" THEN LET N=16: GO TO 480
   470 LET Begin=Begin+1: GO TO 440
   480 LET Digit=0: LET End=Begin: LET PointD=0: LET K=1/N
   490 LET End=End+1
   500 IF LEN (a$)<End THEN GO TO 620
   510 GO SUB 1440: IF D=-1 THEN GO TO 540
   520 IF PointD=0 THEN LET Digit=Digit*N+D: GO TO 490
   530 LET Digit=Digit+D*K: LET K=K/N: GO TO 490
   540 IF a$(End) <> "." THEN GO TO 570
   550 IF PointD=1 THEN GO TO 1420
   560 LET PointD=1: GO TO 490
   570 IF End=Begin+1 THEN GO TO 1420
   580 LET b$= STR$ (Digit)
   590 IF Begin=1 THEN LET a$= b$+a$(End TO LEN (a$)): GO TO
       610
   600 LET a$=a$(1 TO Begin-1)+b$+a$(End TO LEN (a$))
   610 LET Begin=Begin+ LEN (b$): GO TO 450
   620 IF End=Begin+1 THEN GO TO 1420
   630 LET b$= STR$ (Digit)
   640 IF Begin=1 THEN LET a$=b$: GO TO 660
   650 LET a$=a$(1 TO Begin-1)+b$
   660 REM -----= CALCULATE =-----
   670 LET L= VAL (a$)
   680 IF Memory <> 0 THEN LET M(Memory)=L
   690 REM ---= PRINT RESULT =---
   700 INK 7
   710 REM ------= BINARY =------
   720 IF OutBin=0 THEN GO TO 970
   730 IF PEEK AdrL <> 0 THEN GO TO 830
   740 LET Result= PEEK (AdrL+2)+ PEEK (AdrL+3)*256: LET b$=""
   750 IF PEEK (AdrL+1)=255 THEN LET Result=65536-Result:
       PRINT "-";
   760 PRINT "%";
   770 LET j=32768
   780 IF Result >= j THEN LET Result=Result-j: LET b$=b$+"1":
       GO TO 800
   790 LET b$=b$+"0"
   800 IF j>1 THEN LET j= INT (j/2+0.5): GO TO 780
   810 IF ( LEN (b$)>1 AND b$(1)="0") THEN LET b$=b$(2 TO LEN
       (b$)): GO TO 810
   820 PRINT b$: GO TO 970
   830 IF PEEK (AdrL+1)>128 THEN PRINT "-";
   840 PRINT "%1.";
   850 LET E= ( PEEK AdrL)-128: LET b$=""
   860 FOR i=1 TO 4
   870 LET byte= PEEK (Adrl+i): IF (i=1 AND byte >= 128) THEN
       LET byte=byte-128
   880 LET j=128: IF i=1 THEN LET j=64
   890 IF byte >= j THEN LET byte=byte-j: LET b$=b$+"1": GO TO
       910
   900 LET b$=b$+"0"
   910 IF j>1 THEN LET j= INT (j/2+0.5): GO TO 890
   920 NEXT i
   930 IF ( LEN (b$)>1 AND b$( LEN (b$))="0") THEN LET b$=b$(1
       TO ( LEN (b$)-1)): GO TO 930
   940 PRINT b$;
   950 IF E=1 THEN PRINT : GO TO 970
   960 PRINT "*2^";E-1
   970 REM ------= DECIMAL =------
   980 IF OutDec=0 THEN GO TO 1000
   990 PRINT L
  1000 REM ----= HEXADECIMAL =----
  1010 IF OutHex=0 THEN GO TO 70
  1020 IF PEEK AdrL <> 0 THEN GO TO 1130
  1030 LET Result= PEEK (AdrL+2)+ PEEK (AdrL+3)*256: LET b$=""
  1040 IF PEEK (AdrL+1)=255 THEN LET Result=65536-Result:
       PRINT "-";
  1050 PRINT "#";
  1060 LET j=4096
  1070 LET Digit= INT (Result/j): LET Result=Result-Digit*j
  1080 IF Digit <10 THEN LET c$= STR$ (Digit): LET b$=b$+c$:
       GO TO 1100
  1090 LET c$= CHR$ (Digit-10+ CODE "A"): LET b$=b$+c$
  1100 IF j>1 THEN LET j= INT (j/16+0.5): GO TO 1070
  1110 IF ( LEN (b$)>1 AND b$(1)="0") THEN LET b$=b$(2 TO LEN
       (b$)): GO TO 1110
  1120 PRINT b$: GO TO 70
  1130 IF PEEK (AdrL+1)>128 THEN PRINT "-";
  1140 PRINT "#";
  1150 LET E= ( PEEK AdrL)-128: LET b$=""
  1160 LET Ost= ABS E - INT ( ABS E/4)*4
  1170 LET Shift=0: IF Ost=0 THEN GO TO 1200
  1180 IF E<0 THEN LET Shift=Ost: GO TO 1200
  1190 LET Shift=4-Ost
  1200 FOR i=1 TO 4: LET R(i)= PEEK (AdrL+i): NEXT i: LET
       R(5)=0: IF R(1)<128 THEN LET R(1)=R(1)+128
  1210 FOR i=1 TO Shift
  1220 LET Carry=0
  1230 FOR j=1 TO 5
  1240 LET Temp=0: IF R(j)/2 <> INT (R(j)/2) THEN LET Temp=128
  1250 LET R(j)=Carry+ INT (R(j)/2)
  1260 LET Carry=Temp
  1270 NEXT j: NEXT i
  1280 FOR i=1 TO 5
  1290 LET byte= R(i)
  1300 LET j=16
  1310 LET Digit= INT (byte/j): LET byte=byte-Digit*j
  1320 IF Digit <10 THEN LET c$= STR$ (Digit): GO TO 1340
  1330 LET c$= CHR$ (Digit-10+ CODE "A")
  1340 LET b$=b$+c$: IF LEN b$=1 THEN LET b$=b$+"."
  1350 IF j>1 THEN LET j= INT (j/16+0.5): GO TO 1310
  1360 NEXT i
  1370 IF ( LEN (b$)>3 AND b$( LEN (b$))="0") THEN LET b$=b$(1
       TO ( LEN (b$)-1)): GO TO 1370
  1380 PRINT b$;
  1390 IF E-4+Shift=0 THEN PRINT : GO TO 70
  1400 PRINT "*16^";(E-4+Shift)/4
  1410 GO TO 70
  1420 REM -------= ERROR =-------
  1430 INK 7: PRINT "Error in expression": BEEP .1,0: GO TO 70
  1440 REM -= CONVERT ONE DIGIT =-
  1450 LET D=-1
  1460 IF N=16 THEN GO TO 1490
  1470 IF (a$(End)="0" OR a$(End)="1") THEN LET D= VAL
       (a$(End)): RETURN
  1480 RETURN : REM WITH D=-1
  1490 IF (a$(End) >= "0" AND a$(End) <= "9") THEN LET D= VAL
       (a$(End)): RETURN
  1500 IF (a$(End) >= "a" AND a$(End) <= "f") THEN LET D= CODE
       (a$(End))- CODE "a"+10: RETURN
  1510 IF (a$(End) >= "A" AND a$(End) <= "F") THEN LET D= CODE
       (a$(End))- CODE "A"+10: RETURN
  1520 RETURN : REM WITH D=-1

А как она работает?

Рассмотрим на примере, как работает эта программа. Пусть пользователь ввёл такую строку:

=%# m(2)=m(1)+SIN (%.011101+#1.7ac–2)

Сначала проверяется: может быть, это пустая строка (и тогда надо очистить экран), или это команда «Q» (тогда надо выйти в TR-DOS). В данном случае это не так. Тогда исходная строка печатается, и проверяется, есть ли в её начале команда для указания формата вывода («=» или «==»). В данном случае такая команда есть, она устанавливает вывод результата в двоичном и шестнадцатеричном виде. Команда обрабатывается и удаляется из строки, после чего в ней остаётся следующее:

m(2)=m(1)+SIN (%.011101+#1.7ac–2)

Далее проверяется, есть ли в начале строки команда помещения результата вычислений в ячейку памяти. В данном случае такая команда есть, результат должен быть помещён во вторую ячейку. Этот факт запоминается, команда удаляется из строки, после чего в ней остаётся следующее:

m(1)+SIN (%.011101+#1.7ac–2)

Затем двоичные и шестнадцатеричные числа в строке заменяются на десятичные:

m(1)+SIN (0.453125+1.4794922–2)

И, наконец, происходит вычисление этого выражения с помощью функции VAL. Результат помещается в переменную L и, при необходимости, в указанную ячейку памяти (в данном случае — в M(2)). Остаётся вывести его в одной или нескольких системах счисления, заданных пользователем (в данном случае — в двоичной и шестнадцатеричной).

Если результат нужно вывести в десятичном виде, никакого специального преобразования не требуется — он просто печатается командой PRINT L. Но для вывода в двоичном или шестнадцатеричном виде необходимо преобразовать результат в этот вид. Исходные данные для этого — внутреннее 5-байтное представление результата. Но как до него добраться? Так как переменная L, в которой хранится результат, является первой переменной программы, то её значение хранится в самом начале области переменных (точнее, по смещению 1: один байт уходит на имя переменной). А адрес начала этой области указан в системной переменной VARS, расположенной по адресу 23627.

После преобразования результат печатается, и программа готова к вычислению нового выражения.

Страница Ивана Рощина > Статьи >