ПРОФЕССИОНАЛЬНЫЙ ПОДХОД
Уважаемые читатели, если Вы читаете "ZX-РЕВЮ" не первый год, то должны быть знакомы с серией статей Стива Тернера под названием "Профессиональный подход", печатавшихся в прошлом году.
При всем уважении к английской школе программирования для "Спектрума" мы должны сказать что и у нас немало хороших программистов. Только скромность не позволяет честно признать что для "Спектрума" у нас их сейчас может быть уже и больше, а не видно их работы только потому что нет инфраструктуры многочисленных фирм способных приобретать их разработки и тиражировать массовыми тиражами.
Нет пока у нас и средств массовой информации, способных объединить миллионы людей, охваченных единой страстью Мы прекрасно понимаем что роль ZX-РЕВЮ в этом деле конечно какая-то есть, но это не более, чем капля в море. Мы ведь не охватываем и сотой доли процента тех, кто в этом нуждается.
В нашем портфеле с каждым днем становится все больше и больше интересных статей от отечественных программистов, которым есть чем поделиться с многочисленными любителями, как с начинающими, так и с имеющими некоторый опыт.
Сегодня мы возвращаемся к рубрике "Профессиональный подход", но уже на другом уровне. Теперь мы открываем ее для наших отечественных специалистов, которым есть что сказать своим коллегам. Если и у Вас есть идеи и Вы можете их изложить доступно для понимания широкими массами, мы открыты и для вас. Работа оплачивается.
ОБРАБОТКА ОШИБОК В БЕЙСИКЕ
Среди программистов более или менее освоивших Бейсик, прошитый в ПЗУ "Спектрума", бытует мнение, что невозможно на Бейсике создать хорошую "качественную" программу, которая к тому же выглядела бы достаточно "фирменно". Хорошая программа -на 5 баллов - это, как правило, программа в машинных кодах. Существует также достаточно много расширений возможностей обычного Бейсика. Это LASER-BASIC, BETA-BASIC, MEGA-BASIC и другие. Однако все их надо догружать с ленты, при этом результирующая программа получается достаточно громоздкой. Эти расширения Бейсика хороши и оправданы в том случае, если Вы создаете например серьезную игровую программу, заведомо претендующую на "фирменность". Зачастую же для повседневных задач не нужны богатые возможности расширений Бейсика. Прелесть Бейсика-ПЗУ в том, что он всегда сразу же готов к работе.
Что такое "повседневные задачи"? Ну, например я применяю компьютер для обучения детей устному счету, обучения азбуке. Одно дело, когда этому учит мама или папа, и совсем другое дело для ребенка вести диалог с КОМПЬЮТЕРОМ! Эффект обучения здесь на порядок выше. Компьютер может применяться в школе с первого до последнего класса. (На эту тему можно говорить бесконечно.) А студентам очень пригодится, например, при расчетах курсовых проектов. Это и есть "повседневные задачи". Вы освоили Бейсик ПЗУ и в состоянии написать нехитрую программу для той задачи, которая сейчас нужна. Задача решена и программа, может быть, больше никогда не понадобится. А может быть Ваш труд и не пропадет даром, а заинтересует других. Но это произойдет вероятнее в том случае, если программа выглядит "фирменно". Помните: встречают по одежке. Я применяю несколько простых приемов, которыми готов поделиться. Освоив их, Вы сможете придать "фирменный" вид любой самой простой и примитивной Бейсик-программе. Да Вы и сами получите удовольствие от пользования такой программой.
Итак, первый "фирменный" прием.
ПРЕДОТВРАЩЕНИЕ ОСТАНОВКИ БЕЙСИК ПРОГРАММЫ
Мало кому нравятся Бейсик программы, которые останавливаются при первой встретившейся ошибке или случайном нажатии клавиши "BREAK" Кроме неудобства в работе, такие программы выглядят "нефирменно", в отличие от программ в машинных кодах. Однако существует способ, при помощи которого можно устранить остановку программы от клавиши "BREAK" или при встретившейся ошибке, причем в зависимости от ошибки и места в программе, где эта ошибка произошла, возможна различная, заранее запланированная реакция программы.
Этот способ, по-моему, незаслуженно недостаточно распространен среди программистов, хотя дает отличные результаты. Речь идет о программе в машинных кодах '^N ERROR GO TO". Те, у кого есть фирменная программа "SUPERCODE" или ее более поздние версии, возможно знают об упомянутом блоке кодов, но не применяют его из за недостаточного удобства в работе. Методика, изложенная ниже, позволит достаточно легко и эффективно пользоваться этой программой, а для тех, у кого нет программы "SUPERCODE", приводится блок кодов "ON ERROR GO TO". Для тех, кто интересуется программированием в машинных кодах, подробно описывается его работа, а если Вы не интересуетесь машинными кодами, то можете пропустить описание работы блока "ON ERROR GO TO". Прочитав раздел о примере использования этого блока, Вы сможете "пристыковать" его к любой имеющейся у Вас Бейсик-программе. Итак, сначала о блоке кодов "ON ERROR GO TO".
1. Блок кодов "ON ERROR GO TO"
Этот блок имеет длину 73 байта. Он является релоцируемым, то есть его можно загружать в любое место памяти. Вызывается он обычным способом, то есть при помощи RANDOMIZE USR ADDR, где ADDR - это начальный адрес загрузки блока кодов.
Рассмотрим работу блока, рас положив его для примера в буфере принтера начиная с адреса 23296 (#5B00).
В листинге блока кодов справа число в скобках - это ссылки на комментарии по работе блока, описание которой дано ниже.
Текст программы "ON ERROR GO TO"
5B00 |
CD7C00 |
CALL |
#007C |
(1) |
5B03 |
3B |
DEC |
SP |
(2) |
5B04 |
3B |
DEC |
SP |
|
5B05 |
E1 |
POP |
HL |
(3) |
5B06 |
010F00 |
LD |
BC,#000F |
(4) |
5B09 |
09 |
ADD |
HL,BC |
(5) |
5B0A |
EB |
EX |
DE,HL |
(6) |
5B0B |
2A3D5C |
LD |
HL,(#5C3D) |
(7) |
5B0E |
73 |
LD |
(HL),E |
(8) |
5B0F |
33 |
INC |
HL |
|
5B10 |
72 |
LD |
(HL),D |
|
5B11 |
C9 |
RET |
(9) |
|
5B1E |
3B |
DEC |
SP |
(10) |
5B13 |
3B |
DEC |
SP |
|
5B14 |
CD8E02 |
CALL |
#028E |
(11) |
5B17 |
7B |
LD |
A,E |
(12) |
5B18 |
FEFF |
CP |
#FF |
|
5B1A |
20F8 |
JR |
NZ,#5B14 |
(13) |
5B1C |
3A3A5C |
LD |
A,(#5C3A) |
(14) |
5B1F |
FEFF |
CP |
#FF |
|
5B21 |
2821 |
JR |
Z,#5B44 |
(15) |
5B23 |
FE07 |
CP |
#07 |
(16) |
5B25 |
2B1D |
JR |
Z,#5B44 |
|
5B2T |
FE08 |
CP |
#08 |
(17) |
5B39 |
2819 |
JR |
Z,#5B44 |
|
5B2B |
3C |
INC |
A |
(18) |
LD (#5C81),A
LD (IY+0),#FF
LD HL,#0000
LD (#5C42),HL
XOR A
LD (#5C44),A
SET 7,(IY+1)
JP #1B7D
INC SP
INC SP
JP #1303
5В2С 32815C 5B2F FD3600FF
5B33 210000 5B36 22425C 5B39 AF
5B3A 32445C 5B3D FDCB01FE
5B41 C37D1B 5B44 33
5B45 33 5846 C30313
Блок "ON ERROR GO TO" состоит из двух частей. Собственно программа по обработке ошибки начинается с адреса #5B12 (23314). А с адреса #5B00 (23296) расположена процедура привязки блока кодов к тем адресам, в которых он находится. Привязка осуществляется следующим образом.
Сначала вызывается подпрограмма из ПЗУ (1). По указанному адресу #007C расположена одна единственная команда: RET. При выполнении инструкции CALL на стек компьютера помещается адрес ячейки, в которой находится следующая за CALL команда, то есть в нашем случае #5B03 (23299), а указатель стека - регистр SP - уменьшает свое значение на два.
Встречая инструкцию RET, выполнение программы продолжится с адреса, взятого со стека - это #5B03, а указатель стека увеличит свое значение на два. Но число #5B03 не стирается из памяти. Чтобы возвратить его, производится уменьшение указателя стека на два (2) и число со стека записывается в регистр HL (3). (При этом указатель стека опять увеличивается на два, то есть возвращается к своему прежнему значению.) Теперь в регистре HL находится число #5B03 (23299).
Далее (4) в регистр BC заносится величина смещения #000F (015) байт и добавляется к содержимому регистра HL (5). Теперь в регистре HL будет #5B03+#000F=#5B12 (23299+15=23314). Это адрес начала программы по обработке ошибки.
Если расположить блок кодов "ON ERROR GO TO" с любого другого адреса, то в результате действий (1)...(5) в регистре HL будет адрес начала программы по обработке ошибки. Далее этот адрес пересылается из регистра HL в регистр DE (6) а в регистр HL загружается (7) двухбайтное число из ячеек #5C3D, #5C3Е (23613, 23614). Это адрес системной переменной ERR SP. При ошибке происходит переход на адрес, который содержится на стеке в ячейке, адрес которой находится в ERR SP. До работы программы "ON ERROR GO TO" там был адрес 4867 (#1303) - это в ПЗУ подпрограмма вывода сообщения об ошибке. Этот адрес перехода подменяется на новый, то есть на #5B12 (23314), записываемый на стек побайтно из регистров E и D (8). После этого происходит возврат в Бейсик-программу (9).
На этом подготовительная часть закончена. Если ошибки при работе Бейсик-программы не происходит, то все работает обычным порядком, как и без программы "ON ERROR GO TO". А если происходит ошибка, то компьютер передает управление на адрес, записанный на стеке в паре ячеек, указанных в системной переменной ERR SP, то есть на #5B12. При этом происходит следующее.
Вначале указатель стека уменьшается на два, чтобы не испортить стек при работе блока кодов "ON ERROR GO TO" (10). Затем вызывается подпрограмма из ПЗУ "KEY-SCAN" (11). Результат действия этой подпрограммы отражается в регистре DE. Точнее, в регистре E - номер нажатой клавиши. Если не нажато ни одной клавиши, то в регистре E - #FF (255) (аналогично оператору IN в Бейсике).
Сравнивая содержимое регистра E с #FF, предварительно переслав его (12) в аккумулятор A, определяется факт нажатия на какую-либо клавишу. Если результат сравнения не ноль, то есть нажата какая-нибудь клавиша (это может быть, например, клавиша "BREAK"), то зацикливается подпрограмма "KEY-SCAN", приостанавливая дальнейшие действия. Как только клавиша будет отпущена, в регистре E появится число #FF и продолжится работа программы "ON ERROR GO TO"
Далее анализируется произошедшая ошибка, путем проверки содержимого ячейки системной переменной ERR NR, расположенной по адресу #5C3A (23610). Это код ошибки минус 1. Для этого содержимое ERR NR пересылается в аккумулятор (14). Если нет ошибки, а это может произойти в случае логичного завершения программы и ее запланированной остановки в конце (0 OK), то есть код ошибки равен нулю, то содержимое ячейки 23610 будет 0-1 (что для одного байта эквивалентно 256-1) то есть 255 (#FF). В этом случае управление будет передано (15) на адрес #5B44 (23364). Здесь (26) возвращается прежнее значение указателю стека SP и осуществляется переход на подпрограмму вывода сообщения об ошибке (27) (в нашем случае это будет сообщение 0 OK), как это было бы при обычной работе Бейсик-программы.
Аналогичные действия будут и в случае появления ошибки с кодом 8 (8 END of file), (то есть PEEK 23610=7), такая ошибка может произойти при работе с внешней памятью, а также в случае появления ошибки с кодом 9 (9 STOP statement), (то есть PEEK 23610=8), если в тексте программы стоит оператор STOP. Сравнивается содержимое аккумулятора с числом 7 и с числом 8 и в случае совпадения, управление передается (16), (17) на адрес #5B44 (23364).
Дальнейшие действия являются подготовкой к запуску Бейсик-программы с заданной строки. Они необходимы для корректной дальнейшей работы интерпретатора Бейсика.
Для этого прежде всего в ячейку, отведенную для системной переменной ERR NR, заносится число #FF, имитируя отсутствие ошибки (20). Здесь следует пояснить, что при работе интерпретатора Бейсика в "Спектруме" в регистровой паре IY находится число #5C3A (23610) - адрес системной переменной ERR NR.
Далее в два байта системной переменной NEW PPC (ее адрес - #5C48) записывается номер Бейсик-строки, на которую должен быть сделан переход. Для того, чтобы осуществить это, необходимо предварительно номер строки для перехода занести (21) в регистр HL и затем (22) содержимое HL переслать по адресу #5C48. Здесь отметим для себя, что номер Бейсик строки для перехода находится в ячейках #5B34, #5B35 (2334823349). К этому моменту мы еще вернемся позже.
Затем обнуляется ячейка системной переменной NS PPC - это номер оператора в строке, на которую будет сделан переход. Сначала обнуляется аккумулятор A (23) и затем содержимое A пересылается в NS PPC - ячейку #5C44 (23620).
Теперь надо установить флаг синтаксиса: 7-й разряд системной переменной FLAGS -управляющие флаги Бейсика, имитируя положительный результат проверки Бейсик-строки на синтаксис (24). Адрес FLAGS - #5C3B (23611) (или IY+1).
Теперь подготовка Бейсик-системы закончена и можно запускать интерпретатор Бейсика (25). Таким образом, Бейсик-программа будет запущена со строки, номер которой задан двухбайтным числом по адресу #5B34 (23348).
Некоторые моменты в работе блока кодов могут показаться излишними, но благодаря им блок "ON ERROR GO TO" устойчиво и надежно работает в разных режимах.
Для того, чтобы получить блок кодов "ON ERROR GO TO", наберите следующую Бейсик-программу:
10 LET n=23296: LET s=0 20 FOR x=n TO n+72 30 READ y 40 POKE x,y 50 LET s=s+y 60 NEXT x
70 IF s<>7298 THEN PRINT FLASH 1;"ERROR": STOP 80 SAVE "on err" CODE 23296,73
100 DATA 205,124,0,59,59,255,1,15,0,9,235,42,61,92,115,35,114,201
110 DATA 59,59,205,142,2,123,254,255,32,248,58,58,92,254,255,40,33,254,7,40,29,254,8,40,25 120 DATA 60,50,129,92,253,54,0,255,33,0,0,34,66,92,175,50,68,92,253,203,1,254 130 DATA 195,125,27,51,51,195,3,19
После старта, если Вы все набрали правильно, программа сформирует блок кодов и
выдаст его для записи на магнитофон.
Для того, чтобы показать работу программы "ON ERROR GO TO", нам понадобится демонстрационная Бейсик-программа. Это будет программа под названием "BEEPER". Задача, решаемая ей, примитивна и не представляет практического интереса, но на этом примере будут продемонстрированы возможности программы "ON ERROR GO TO" и методы ее практического использования.
2. Программа "BEEPER"
Текст программы:
1 GO TO 100
2 BORDER 7: PAPER 7: INK 0: aS
3 LOAD "on err" CODE 23296
4 RUN
5 SAVE "BEEPER" LINE 2
6 SAVE "Оп err" CODE 23296,73
7 STOP
20 INPUT ;
22 PRINT #0; TAB 8; FLASH 1;"press any key" 24 PAUSE 0 26 RETURN
100 BORDER 1: PAPER 7: INK 0: CLS
110 PRINT AT 12,8; PAPER 2; INK 7; BRIGHT 1;" B E E P E R 120 GO SUB 20
130 BORDER 7: CLS: PRINT AT 21,0; 1000 REM "beeper"
1010 INPUT "Enter tune (-60...69): ",t 1020 PRINT "Tune=";t
1030 PRINT #0;TAB 8;INVERSE 1;" B E E P "
1040 FOR n=1 TO 50
1050 BEEP .O7,t
1060 NEXT n
1070 GO SUB 20
1080 GO TO 1000
После того, как Вы наберете программу, запустите ее со второй строки RUN 2 и загрузите блок кодов "on err" CODE. После окончания загрузки нажмите "BREAK" и выгрузите готовую программу на ленту, выполнив RUN 5.
Теперь можете командой RUN запустить программу сначала. После появления "заставки", нажмите любую клавишу. В ответ на запрос о величине тона введите число от -60 до 69 и нажмите любую клавишу. Вы услышите прерывистый звук, продолжающийся около пяти секунд, затем программа зацикливается, запрашивая следующую величину тона.
Теперь несколько слов о ситуациях, при которых происходит остановка программы.
Остановка с сообщением "L BREAK into program" произойдет при нажатии на клавишу "BREAK" в режиме ожидания, когда появляется мигающая надпись "press any key" или в момент звукового сигнала. Остановка с сообщением "D BREAK - CONT repeats" произойдет, если нажать "BREAK" в момент ожидания загрузки блока кодов (строка 2). Если при загрузке блока кодов произойдет ошибка магнитофона, то программа остановится с сообщением "R tape loading error". Программу можно остановить, если на запрос о величине тона вместо числа ввести букву, например а,Ь,... В этом случае остановка произойдет с сообщением "2 Variable not found". Если введенная величина тона будет ниже 60 или выше 69, то программа остановится при попытке выполнить оператор BEEP с сообщением "B integer out of range".
Чтобы запустить программу "ON ERROR GO TO", изменим строку 1:
1 RANDOMIZE USR 23296 GO TO 100
Теперь при любой ошибке, произошедшей после команды RUN, программа будет запускаться со строки, номер которой задан двухбайтным числом по адресу 23348. Так как там 0, то программа перезапустится с начальной строки (в нашем случае с первой строки). Теперь остановить программу невозможно, Вы можете убедиться в том, что при любой попытке остановки программа просто перезапустится заново.
Это не совсем тот эффект, который нам необходим, но на этом этапе становится ясно, что остановить программу после старта невозможно, можно только перезагрузить ее с ленты, а это неудобно при отладке программы. Поэтому надо позаботиться о возможности остановки программы в процессе отладки. Это можно сделать, например, введя такой "жучок":
25 IF INKEY$ "q" THEN STOP
Теперь, когда на экране появляется мигающая табличка "press any key", то программу можно остановить, нажав клавишу "Q".
Для того, чтобы инициировать программу "ON ERROR GO TO ", надо, чтобы было выполнено: RANDOMIZE USR 23296. Эту команду можно подставить в начало строки, с которой происходит запуск программы (строка 1) Далее, на разных этапах выполнения программы надо изменять содержимое ячеек 23348,23349, чтобы управлять переходом по ошибке согласно задуманной логике. Например, добавим строки:
115 POKE 23348,24 POKE 23349, 0 1000 POKE 23348.242 POKE 23349.3 1065 POKE 23348.0 POKE 23349,0
Строка 115 блокирует клавишу "BREAK", исключая остановку программы в режиме "заставки", возвращая ее все время на строку 24 - ожидание нажатия какой-нибудь клавиши. Строка 1000 определяет возврат на строку 1010 при ошибке ввода данных (PEEK 23348+256*PEEK 23349=1010). Строка 1065 определяет возможность перезапуска программы сначала при нажатии клавиши "BREAK", когда появляется мигающая надпись "press any key".
Теперь можно считать, что мы добились требуемой логики работы программы. Предотвращается остановка программы при ошибке ввода данных. Кроме того, можно прервать звуковой сигнал в момент исполнения (когда появляется табличка "BEEP"). А когда исполнение закончится можно вообще перезапустить программу, нажав "BREAK".
Однако следует отметить еще один момент. Определенное неудобство доставляет перевод номера строки в двухбайтную форму. Это приходится делать вручную. К тому же если придется переделывать программу в процессе отладки, изменяя нумерацию строк, то опять приходится пересчитывать данные для POKE 23348, POKE 23349.
Задачу можно значительно упростить если вспомнить о том, что двухбайтный конвертер в "Спектруме" уже есть. Это оператор RANDOMIZE, который задает начальное значение для функции случайной величины RND. Подав команду RANDOMIZE n, мы устанавливаем системную переменную SEED, используемую для вычисления очередного значения функции RND. SEED расположена по адресу 23670 и занимает два байта. В ячейке 23670 находятся младший, а в ячейке 23671 - старший байты числа n, которое используется с оператором RANDOMIZE. Подайте команду RANDOMIZE 1010. Теперь сделайте:
PRINT PEEK 23670 PRINT PEEK 23671
- получите 242 и 3
Теперь усовершенствуем нашу программу, добавив строки:
10 POKE 23348, PEEK 23670: POKE 23349, PEEK 23671: RETURN
Изменим строки
115 RANDOMIZE 24: GO SUB 10
1000 RANDOMIZE 101O: GO SUB 10
1065 RANDOMIZE 1: GO SUB 10
Теперь, с точки зрения программирования, задача упростилась. Здесь только надо отметить, что нельзя подавать команду RANDOMIZE 0, так как в этом случае переменная SEED принимает не значение 0, а становится равной другой системной переменной FRAMES - счетчика кадров, обеспечивая практически случайное число. Вместо RANDOMIZE 0 можно подавать RANDOMIZE 1, так как программа все равно обычно начинается со строки 1. (Если же у Вас присутствует нулевая строка и Вы хотите стартовать именно с нее, то просто подставьте в требуемое место, как и раньше, POKE 23348,0: POKE 23349, 0 )
Сейчас можно удалить строку 25, которая нужна была для отладки программы. Теперь единственным путем, позволяющим остановить программу, является нажатие "BREAK" при загрузке с ленты, в тот момент, когда Бейсик-программа уже загружена и ожидается ввод блока кодов "on err" (строка 2).
При желании можно ликвидировать и эту возможность. Для этого блок кодов "ON ERROR GO TO" можно расположить внутри Бейсик-программы в нулевой строке. Наберите:
1 REM
а после REM - 73 пробела или любых других символов. Затем сделайте POKE 23756,0. Первая строка стала нулевой. Теперь выполните LOAD "on err" CODE 23760.
Затем измените строки:
2 RANDOMIZE 1: GO SUB 10: GO TO 100
10 POKE 23812,PEEK 23670: POKE 23813, PEEK 23671: RANDOMIZE USR 23760 RETURN
Теперь, когда блок "ON ERROR GO TO" расположен в новом месте (начиная с адреса 23760), ячейками в которых задана строка для перехода по ошибке, будут 23812, 23813.
Строки 1, 3, 6 - удалите, они больше не нужны, так как теперь наша программа состоит из одного блока вместо двух и "горячий" старт (RUN 2) совпадает с "холодным" стартом (RUN). В этом варианте изменен также способ инициирования блока кодов "ON ERROR GO TO". Команда RANDOMIZE USR 23760 помещена теперь не в первую (стартовую) строку, как раньше, а в строку 10. Поэтому включение блока в работу происходит теперь в любом случае, когда выполняется команда GO SUB 10, независимо от того, с какой строки Вы запустите Вашу программу. Такой вариант более универсален.
Мы рассмотрели работу блока кодов "ON ERROR GO TO" на примере маленькой программы. В больших программах может потребоваться более сложная логика перехода при ошибке. Для этого используйте ячейку системных переменных 23681, куда заносится код ошибки при работе блока "ON ERROR GO TO". Анализируя PEEK 23681, Вы можете организовать переход на нужную строку. Для примера измените строку 1000 программы "BEEPER".
1000 RANDOMIZE 2000 GO SUB 10
Добавьте строки:
2000 IF PEEK 23681=11 THEN INPUT ;: PRINT #0; PAPER 2; INK 7; BRIGHT 1; " W A R N I N G : 60<Tune<69 ": BEEP 1,0: PAUSE 100
2010 GO TO 1010
Теперь в том случае, если вводимая величина будет выходить за пределы -60...+69, появится предупредительная табличка со звуковым сигналом. Конечно, проверку на допустимые пределы логичнее организовать при помощи обычного Бейсика, просто этот пример показывает, как можно использовать код ошибки. Хотя надо сказать, что в своих программах мне еще ни разу не приходилось этим приемом пользоваться.
В заключение хочу сказать, что при отладке программ с блоком "^N ERROR GO TO" почаще делайте RUN 5 (сохранение программы на ленте). В результате ошибочных действий может случиться так, что Вы не сможете остановить программу. Тогда останется только загрузить предыдущий вариант. Если предполагаются значительные изменения в программе, то временно в начало строки 10 подставьте RETURN: это отключит блок "ON ERROR GO TO". Кроме того всегда предусматривайте "жучок" для возможности остановки программы типа строки 27 в программе "BEEPER".
В следующий раз мы поговорим о структуре Бейсик программ, о том, с чего практически начинать написание новой программы, то есть когда в уме или на бумаге план уже достаточно "созрел" и Вы включили компьютер, чтобы набивать текст программы, а также о том, как облегчить себе жизнь, предусмотрев элементарный сервис для себя.