© Иван Рощин, Москва
| ZXNet | : 500:95/462.53 |
| : bestview@mtu-net.ru | |
| WWW | : http://www.ivr.da.ru |
(«Радиомир. Ваш
компьютер» 1/2002)
Дата последнего редактирования: 22.02.2003.
Введение
Инструкция пользователя
Листинг программы
А как она работает?
Скачать программу в формате hobeta (2 КБ ZIP)
Иногда требуется провести вычисления, в которых участвуют не только десятичные числа, но и двоичные или шестнадцатеричные, или просто нужно перевести число из одной системы счисления в другую. Как это сделать?
В Бейсике можно работать только с десятичными числами и с целыми двоичными числами в диапазоне 065535 (с помощью функции BIN). В ассемблере ZX ASM (и в некоторых других ассемблерах) имеется встроенный калькулятор, поддерживающий все три системы счисления, но он может работать лишь с целыми числами в диапазоне 065535. А что делать, если числа дробные или выходят за пределы этого диапазона?
Пришлось написать свою
После запуска вводите вычисляемое выражение точно так же, как если бы вы вычисляли его в Бейсике. При записи чисел можно использовать все три системы счисления. Двоичные числа предваряются символом «%», шестнадцатеричные символом «#». Буквы AF, используемые при записи шестнадцатеричных чисел, могут быть как прописными, так и строчными. Названия используемых функций, а также название константы 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)=2x24xy+y2 в нескольких точках. После загрузки программы добавьте в неё такую строку:
5 DEF FN F(X,Y)=2*X*X4*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») можно выйти в
Теперь расскажу об обработке ошибочных ситуаций. Ошибки, связанные с неправильным указанием параметров в командах «=» и «==», а также ошибки при записи двоичных или шестнадцатеричных чисел обрабатываются в программе. При возникновении такой ошибки раздается звуковой сигнал и выводится сообщение «Error in expression».
Остальные ошибки обрабатываются непосредственно интерпретатором Бейсика. При их возникновении выполнение программы останавливается с выводом соответствующего сообщения («Number too big», «Invalid argument» и т.п.). После этого для продолжения работы с программой её надо перезапустить командой «RUN».
ВНИМАНИЕ!
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.7ac2)
Сначала проверяется: может быть, это пустая строка (и тогда надо очистить
экран), или это команда «Q» (тогда надо выйти в
m(2)=m(1)+SIN (%.011101+#1.7ac2)
Далее проверяется, есть ли в начале строки команда помещения результата вычислений в ячейку памяти. В данном случае такая команда есть, результат должен быть помещён во вторую ячейку. Этот факт запоминается, команда удаляется из строки, после чего в ней остаётся следующее:
m(1)+SIN (%.011101+#1.7ac2)
Затем двоичные и шестнадцатеричные числа в строке заменяются на десятичные:
m(1)+SIN (0.453125+1.47949222)
И, наконец, происходит вычисление этого выражения с помощью функции VAL. Результат помещается в переменную L и, при необходимости, в указанную ячейку памяти (в данном случае в M(2)). Остаётся вывести его в одной или нескольких системах счисления, заданных пользователем (в данном случае в двоичной и шестнадцатеричной).
Если результат нужно вывести в десятичном виде, никакого специального
преобразования не требуется он просто печатается командой PRINT
L. Но для вывода в двоичном или шестнадцатеричном виде необходимо
преобразовать результат в этот вид. Исходные данные для этого
внутреннее
После преобразования результат печатается, и программа готова к вычислению нового выражения.