Жариков Л.Н.
Программа для программатора ППЗУ и её оптимизация
Многие читатели заинтересовались опубликованной в выпуске № 2 за этот год программой Р. Барташюса для программатора ППЗУ и предлагают свои изменения. Вызвало нарекания большое количество опечаток - неизменных спутников любой сложной публикации.
Вашему вниманию предлагается обзор предложений. Свой вклад в программу внесли: В.Г. Бендаринов из г. Горького, Г. Мухамедов из г. Ташкента, Д.Ю. Усенков из Москвы, знакомый вам по публикации в том же выпуске, и автор данной статьи.
Сначала ответы на некоторые вопросы. Многих читателей удивило, что в строках 5050, 5060 исходного текста вычисления не выполнены самим автором программы. Очевидно, эти строки специально публиковались Р. Барташюсом в исходном виде для того, чтобы подчеркнуть аддитивные составляющие передаваемых в порт данных, а сложение двух чисел он доверил выполнить читателям.
Вызвало недоумение пользователей и наличие пустых операторов PRINT. Ведь разместить выводимую на экран информацию в Бейсике легко с помощью инструкций АТ, указывающих позицию. Думается, что это было сделано автором для большей простоты и наглядности текста.
Теперь об исправлениях программы, которые предлагаются с целью улучшения её пользовательских качеств и стиля программирования. Нужно сказать, что указанные читателями изменения и дополнения касаются только отдельных сторон её оптимизации и не дают максимального эффекта, который хотелось бы получить.
Если же действительно говорить о существенной оптимизации программ, то это достигается применением сочетания достаточно стандартных способов.
Вот некоторые из способов оптимизации:
- тщательный выбор метода и самого алгоритма (обычно это даёт максимальный эффект);
- выбор типов и структуры данных, которые обеспечивают оптимизацию соответствующих параметров программы (время исполнения, объём занимаемой памяти);
- вычисление везде, где только возможно, числовых выражений;
- раскрытие и другая оптимизация циклов;
- выделение подпрограмм или, наоборот, их явная подстановка в места, критичные по времени выполнения;
- выбор форм операторов языка, обеспечивающих наиболее лаконичное выполнение функций или наименьшее время выполнения;
- использование специфических особенностей реализации языка конкретной ЭВМ;
- написание наиболее критичных по времени участков программы в машинных кодах;
- жертвование сервисом ради уменьшения объёма программы.
Способы приведены приблизительно в той последовательности, в которой их следует применять. Как знают опытные программисты, на практике применение этих способов обычно начинается в обратном порядке, приводя к значительным затратам усилий для достижения того же эффекта, который даёт правильная последовательность.
На примере вызвавшей специфический интерес к программе для программатора ППЗУ, используя некоторые предложения читателей, покажем, как применять приведённые выше приёмы. Будем пользоваться текстом программы Р. Барташюса, приведённым во втором выпуске ВТ.
Первый в перечне способ ни у кого не вызывает сомнений, если это связано с достаточно солидной программой. Имея перед глазами работоспособную версию, метод и саму структуру алгоритма, будем считать пока правильными, как это делают сначала все программисты, и используем второй метод.
Во второй строке исходной программы описан массив D(63,15), no умолчанию являющийся массивом двойной точности. Каждый элемент такого массива занимает в памяти 8 байтов. Если мы вспомним, что данный массив служит для хранения прошиваемых в ППЗУ или загружаемых из ППЗУ байтов, то восьмикратный излишек занимаемой этими данными памяти вызовет некоторое удивление. Для хранения байта, обычно достаточно одного байта памяти!
К сожалению, в Бейсике нет типа данных 'byte', как, например, в языке Турбо-Паскаль. Но нас вполне может устроить описание целочисленного массива D%(63,15), говорящее о том, что каждому элементу будет выделено два байта памяти. С этим можно пока смириться. Всё-таки высвобождается 6 Кбайт памяти! Правда, этот выигрыш несколько уменьшится за счёт хранения в исходном тексте знаков '%'.
Достичь ещё большего выигрыша памяти можно, введя вместо массива D%(63,15) массив символьных строк D$(15) и упаковывая в процессе расчётов байты данных в соответствующие символы одной из шестнадцати строк. Но мы этим пока заниматься не будем. Анализ также показывает, что вообще необходимости в числовых переменных нецелого типа в этой программе нет. Поэтому все переменные заменим на переменные целого типа, добавив к ним символ '%'. Этим действием будет сэкономлена память и увеличено быстродействие, так как операции с целыми числами выполняются существенно быстрее.
Точно таким же образом можно заменить и все числовые константы на константы целого типа, проявляя, однако, известную осторожность, так как диапазон целых чисел в Бейсике достаточно узок [-32768, 32767].
Вследствие этого написать в строках 4040, 5050 и 5060 выражение типа
...,N%-32768%...
вам не удастся. Нужно будет вводить, например, так:
...,N%-32767%-1%... ,
или так
...,N% + &0100000... ,
или так
...,N% + &H8000... .
Ко второму методу оптимизации относится и удаление лишних переменных. Посмотрев на программу несколько внимательнее, можно заметить, что переменная 'B%' в строках 4110, 4120, 4160, 7000, 7050, 7060 используется только для передачи данных подпрограмме в строке 2000. Если от этой весьма короткой подпрограммы избавиться (тоже способ!) и попутно заменить операторы PRINT на знаки вопроса, то соответствующие места будут выглядеть короче:
... 4110-4130 - удалить 4140 ? "ошибка по адресу"; HEX$(I%+KF%) ... 4150-4170 - удалить 4180 ? HEX$(J%); "H" ... 7000-7010 - удалить 7020 ? AT(0%,5%); HEX$(I%) ... 7050-7070 - удалить 7080 ? AT(J%*3% + 6%,5%);HEX$(D%(I%-KF%) AND &HFF) ...
Вычислим и преобразуем теперь, где это возможно, числовые выражения. Таких мест два: строки 4030, 4040 и 5040-5080. Так как эти участки находятся внутри циклов, то это связано и с оптимизацией циклов. Первый цикл после вынесения из него вычислений, не связанных с переменными цикла I% и J%, будет выглядеть так:
... 4000 ? "Ждите..." 4005 K% = KF%*16% + &H8000 4010 FOR I%=0% ТО 63% 4015 L% = I%*16% + K% 4020 FOR J%=0% ТО 15% 4030 N% = L% + J% 4040 РОКЕ -52%,N% 4050 IF CO% = 1% GOTO 4100 4060 D%(I%,J%) = PEEK(-52%) 4070 NEXT J%,I% 4080 – удалить ...
Второй цикл изменится аналогично:
... 5000 ? "ЖДИТЕ..." 5010 K% = KF%*16% + 2048% 5020 FOR I% = 0% ТО 63% 5025 L% = I%*16% + K% 5030 FOR J% = 0% ТО 15% 5040 N% = L% + J% 5045 FOR W% = 0% ТО 5% 5050 РОКЕ -52%, -30720% 5060 РОКЕ -52%, N% + &H8000 5070 РОКЕ -52%, N% 5080 РОКЕ -52%, D%(I%,J%) + 30720% 5090 FOR T = 0 ТО 50 5100 NEXT T,W%,J%,I% 5110-5130 - удалить ...
Заметьте, цикл по W% внесён внутрь циклов по I% и J%. Этим достигается уменьшение количества вычислений вспомогательных переменных K% и L%.
В результате циклы будут выполняться быстрее. Чтобы дополнительно сэкономить ещё, в операторах РОКЕ и РЕЕК можно заменить целочисленные константы на шестнадцатеричные или восьмеричные. Для перевода в другую систему счисления можно воспользоваться функциями OCT$ и HEX$, введенными в непосредственном режиме, например,
? OCT$(-52) 177714 Ok
или
? HEX$(-52) FFCC Ok.
Запустив исправленную таким образом программу на исполнение командой RUN, убедимся, что в результате оптимизации цикл чтения из ППЗУ сократился до 3-4 с, а цикл записи почти не изменился. Попутно отметим, что для проверки совсем не обязательно иметь аппаратную часть программатора ППЗУ, достаточно вставить блок нагрузок в разъём параллельного порта при выключенном питании БК. Это обеспечит чтение упорядоченных данных в массив D% в режиме ввода из ППЗУ и режиму контроля будет что проверять. Записать данные в блок нагрузок, естественно, не удастся.
Дальнейшая надежда уменьшения времени выполнения записи в ППЗУ связана с самим внутренним циклом в строке 5090. К сожалению, сокращение времени исполнения этого пустого цикла, введённого для организации определённой задержки, связано с риском увеличения вероятности возникновения ошибок прошивки ППЗУ. Не зная технических параметров схемы программатора ППЗУ, уменьшать это время не стоит.
К сведению любопытных читателей сообщим, что цикл 5010-5130 без операторов 5050-5080 выполняется 8 мин 05 с, а изменённый цикл 5010-5100 вместе с операторами РОКЕ - 8 мин 21 с. Таким образом, дальнейшая оптимизация рассматриваемого участка практически нецелесообразна.
Чтобы ещё более сократить количество строк, заменим вызовы подпрограммы в строках 3340, 3360, 6110, 7030 на сам оператор ?CHR$(155%), переключающий дисплей в режим 64 или 32 символов в строке. В результате строку 7030 можно удалить, а строка 7020 примет вид
... 7020 ? AT(0%,5%);HEX$(I%);CHR$(155%) ...
Окинем взглядом всю программу целиком (см. в конце статьи). Можно заметить, что выделяются несколько в чём-то однородных участков программы: 70-220, 240-290, 1080-1130, 3080-3130.
Попробуем несколько сократить эти участки, используя специфические особенности реализации Бейсика на БК0010.01 и выбирая формы операторов, обеспечивающие наиболее лаконичное исполнение программой возложенных на неё функций. При этом надо помнить, что включение в текст уникальных возможностей версии языка, равно как и подпрограмм в машинных кодах, ограничивает диапазон компьютеров, на которых программа будет работать без переделки.
Первый из рассматриваемых участков написан нерационально, но очень нагляден. Можно его сжать, если указать строки и колонки, с которых начинать вывод меню, инструкциями АТ и LOCATE.
Введение операторов выбора ON ... GOSUB [GOTO] ... позволяет сократить строки 240-290 второго участка. В результате текст будет выглядеть следующим образом:
... 70 IF KF% = 0% THEN ? "К573РФ1 (IРФ2)" ELSE ? "К573РФ2(II)" 80, 90, 110, 130, 150, 170, 190, 210, 220 - удалить 100 ?AT(0%,3%);"1 ввод с клавиатуры" 120 ?AT(0%,5%);"2 ввод из ППЗУ" 140 ?AT(0%,7%);"3 вывод в ППЗУ" 160 ?AT(0%,9%);"4 корректировка" 180 ?AT(0%,11%);"5 РФ1(IРФ2) или IIРФ2" 200 ?AT(0%,13%);"6 контроль" 230 INPUT T$ 240 ON VAL(T$) GOSUB 6000,4000,5000,3000 250 ON VAL(T$)-4% GOTO 310,330 260-290 - удалить ...
Третий участок 1080-1130, предназначенный для распознавания цифр шестнадцатеричного двухзначного числа, можно сжать, получая значения кодов вводимых символов с помощью функции ASC:
... 1080 IF ASC(Y$)>70 OR U%<>0% OR Y$ = "0" GOTO 1140 1090 C% = ASC(Y$)-55% 1010-1130,1160 - удалить
Четвёртый участок программы 3060-3130 после анализа может быть сокращён за счёт введения распознавания некоторых подмножеств значений переменной R% и объединения условных операторов. Например,
3080 IF FIX(SQR(R%)) = 5% THEN ON R%-24% GOTO 3210,3170,3190 3090 IF R%=81% THEN 3360 ELSE IF R%=67% THEN 3140 ELSE IF R%=8% THEN 3270 ELSE 3060 3100-3130 - удалить ...
Пытливые читатели могут поупражняться в поиске самого лаконичного способа ветвления алгоритма для данных значений R% (25,26,27,81,67,8). Заметьте, что в результате вместо шести программных строк осталось две, но структурность программы не нарушилась.
Теперь можно заняться и сервисом. Правда, при наличии свободной памяти жертвовать мы им пока не собираемся.
Читатель В.Г. Бендаринов (г. Горький) предложил набросок начального участка программы для несколько другой организации меню. Перемещение по меню вверх-вниз осуществляется с помощью клавиш управления курсором, а выбор темы - нажатием клавиши <ввод>. Работа с таким меню более комфортна.
С некоторыми исправлениями в предлагаемом наброске начальный участок программы будет выглядеть следующим образом:
20 DIM D%(63%,15%) 30 SI%=0% 35 Y%=43% 'Вертикальная координата строки меню 40 CLS 50 CO%=0% 60 KF%=SI%*64% 70 IF KF%=0% THEN ? "К573РФ1(IРФ2)" ELSE ? "К573РФ2(II)" 90 LOCATE 5%,5%,0% 'убрать курсор 100 ?"Ввод с клавиатуры" 120 ?AT(5%,7%); "Ввод из ППЗУ" 140 ?AT(5%,9%); "Вывод в ППЗУ" 160 ?AT(5%,11%); "Корректировка" 180 ?AT(5%,13%); "РФ1(IРФ2) или IIРФ2" 200 ?AT(5%,15%); "Контроль" 210 ?AT(5%,17%); "Выход" 220 Y1%=Y% 221 LINE(30%,Y%)-(200%,Y%+20%),1%,B 'Нарисовать рамку темы меню 222 C$=INKEY$ 223 IF C$="" THEN 222 ELSE T%=ASC(C$) 'Между кавычками нет пробела 224 IF T%=26% THEN IF Y%>43% THEN Y%=Y%-20% ELSE Y%=163% 225 IF T%=27% THEN IF Y%<163% THEN Y%=Y%+20% ELSE Y%=43% 226 IF T%=10% THEN IF Y%=163% THEN STOP ELSE 230 227 IF Y%<>Y1% THEN LINE(30%,Y1%)-(200%,Y1%+20%),0%,B 'Стереть рамку темы меню 228 GOTO 220 230 LOCATE 1%,1%,1% 'Восстановить курсор 240 ON (Y%-23%)/20% GOSUB 6000,4000,5000,3000 250 IF Y%=123% THEN 310 ELSE IF Y%<>143% GOTO 40 260 CO%=1% 270 GOSUB 4000 280 ?,,"Для возврата в меню - «ввод»" 'Обратите внимание на запятые 290 IF INKEY$="" THEN 290 ELSE 40 'Между кавычками нет пробела 310 IF KF%=0% THEN SI%=1% ELSE SI%=0% 320 GOTO 40 1000 T%=1% ...
Такая организация работы с меню будет полезна во многих программах читателей и обеспечит большую наглядность выбора.
Можно заметить, что в режимах ввода с клавиатуры и редактирования над вводимой строкой отсутствует нумерация вводимых байтов, облегчающая установку нужной позиции. Такую нумерацию сделать просто. Для этого нужно ввести строку
7075 ? AT (J%*3%+6%,3%); HEX$(J%)
а в строках 3320, 7020, 7080, 7110, 8000, 8020 номер строки экрана заменить на 5%. Работать станет удобнее.
Заметим, что наша оптимизация рассматриваемой программы не была всеобъемлющей: мы не применяли самый эффективный, но и самый сложный способ оптимизации, касающейся уточнения метода и алгоритма. Часто этот приём приводит к проектированию программ заново, поэтому имея уже работоспособную программу, все программисты прибегают к нему в крайнем случае. В начале нашего совместного пути по программе мы приняли авторскую структуру программы и предложенное им разделение на режимы работы. Теперь выделим читателям для проработки несколько идей.
Во-первых, имеется возможность объединения режимов ввода с клавиатуры и корректировки таблицы прошивки в один экранный режим редактирования. Диапазон адресов в микросхеме ППЗУ можно задавать явно.
Во-вторых, контроль правильности хотелось бы выполнять не только в отдельном режиме, но и непосредственно в цикле прошивки. В этом случае циклическое повторение прошивки необходимо только при обнаружении ошибки. Такое решение позволит сэкономить много времени и обеспечить адаптацию программы к особенностям конкретного корпуса микросхем ППЗУ. Дополнительно при неверной прошивке можно программно увеличивать длительность задержки.
Указанные два момента могут существенно повлиять на работу всей программы, как это обычно и случается при использовании первого способа оптимизации.
Одним из последних штрихов является выбор правильного шага перенумерации. На этом тоже можно сэкономить память БК. В начале работы опытные пользователи выбирают шаг перенумерации строк обычно равным 10, чтобы легко вставлять новые строки. Полуотлаженный вариант программы, особенно при недостатке памяти, можно нумеровать и с шагом 5. В публикациях обычно используют шаг 10.
В заключение нашей работы вставим в начало комментарии с наименованием программы автором и участвовавшими в её совершенствовании читателями. Нумерацию строк программы оставим авторскую, так как при желании команда Бейсик-системы
RENUM 10,1,10
быстро выполнит нумерацию с шагом 10, а авторская нумерация позволяет легче выполнять исправления в исходной программе.
Окончательную версию программы записываем на магнитофон командой CSAVE "PPZU".
Подведём итоги нашей короткой экскурсии в мир оптимизации программ. Благодаря настойчивому труду исходный текст программы для программатора ППЗУ стал существенно короче, разнообразие палитры операторов языка шире, более 5 Кбайт памяти осталось для дальнейшего совершенствования программы, сама работа программы значительно быстрее, а диалог - удобнее и понятнее. К сожалению, таких результатов достичь удаётся далеко не всегда.
Терпеливые читатели, добравшиеся до конца и оптимизировавшие программу вместе с нами, получили достаточное количество информации, чтобы самим оптимизировать собственные разработки. При этом они споткнутся о множество подводных камней, которые мы старательно обходили при первом достаточно поверхностном знакомстве с оптимизацией программ. Хочется надеяться, что их труд не пропадёт даром и программы, публикуемые в выпусках серии, будут гораздо рациональнее.
Прощаясь с внимательным читателем, обладающим чувством юмора, приведём несколько советов.
Полезные советы
- Не берите лишнего в памяти! И вообще ничего лишнего не берите! Как мы видели, основной выигрыш по объёму памяти и времени выполнения программы дал в нашем примере правильный выбор типа данных. Ключевой массив D%(63,15) стал целого типа и сэкономил сразу 6 Кбайт памяти. Когда экономия памяти не является главным элементом оптимизации, можно пользоваться принципом умолчания.
- Делайте ваш выбор! Выбирайте тщательнее, но быстрее! Языковая структура типа "выбор" (в Бейсике, оператор ON) может сэкономить много операторов IF. Вложенные операторы IF тоже экономят память.
- Чем меньше переменных, тем лучше! Введение лишних переменных не только расходует оперативную память, затуманивает алгоритм, но обычно уменьшает и скорость работы программы.
- Выносите всё, что можно! Не забудьте только внести обратно главное! Оптимизируя по времени выполнения программу, оптимизируйте тела циклов, вынося из них независимые от переменной цикла выражения.
- Не полагайтесь на компьютер - вычисляйте сами всё, что можно вычислить! Подставляйте результаты в программу. Для ясности программы в публикациях этого можно не придерживаться. Закручивайте формулы как можно круче!
- Пользуйтесь при необходимости не только десятичными константами! Шестнадцатеричные, восьмеричные и двоичные константы часто экономят память и ускоряют процесс вычисления, делая программу ещё более совершенной.
- Оптимизируйте только отлаженные, часто публикуемые и используемые программы, другие сразу после использования бросайте в мусорную корзину! Оптимизация ненужных и неотлаженных программ нужна только для тренировки ваших навыков.
- Подбирайте шаг нумерации строк программы. Помните, "шире шаг" - не для Бейсика БК0010.01.
- Внимайте тщательно! Оптимизация требует внимания и времени. Тестируйте программу при любых самых незначительных изменениях! Самые очевидные вещи в программировании оказываются при ближайшем рассмотрении очевидно неверными. При первом же случайно выполнившемся прогоне программы считайте её отлаженной и сдавайте заказчику.
- Чем больше нетривиальных подсказок в диалоге, тем меньше вопросов у пользователя и оптимальнее интерфейс программы.
- Создавая программу для БК0010, примите все меры к тому, чтобы она не прошла на IBM PC/XT/AT. Для этого вставляйте в программу только те операторы языка, которых нет в других диалектах. В крайнем случае врежьте в программу кусок в машинных кодах. Это положит любого пользователя на лопатки, наверняка обеспечит непереносимость программы на другие ЭВМ и минимальное её использование.
- Публикуйте оптимальные программы, не проверяя на компьютере. Это избавит вас от сомнений. Читатели заодно и проверят!
1 REM Программа для программатора ППЗУ 2 REM *** БК0010.01 *** 3 REM Автор: 4 REM Р.Барташюс 5 REM Модификация: 6 REM В.Г.Бендаринов 7 REM Л.Н.Жариков 8 REM Г.Мухамедов 9 REM Д.Ю.Усенков 10 REM 1990г. 20 DIM D%(63%,15%) 30 SI%=0% 35 Y%=43% 'Вертикальная координата строки меню 40 CLS 50 CO%=0% 60 KF%=SI%*64% 70 IF KF%=0% THEN ? "К573РФ1(IРФ2)" ELSE ? "К573РФ2(II)" 80 LOCATE 5%,5%,0% 'Убрать курсор 100 ? "Ввод с клавиатуры" 120 ?AT(5%,7%); "Ввод из ППЗУ" 140 ?AT(5%,9%); "Вывод в ППЗУ" 160 ?AT(5%,11%); "Корректировка" 180 ?AT(5%,13%); "РФ1(IРФ2) или IIРФ2" 200 ?АТ(5%,15%); "Контроль" 210 ?AT(5%,17%); "Выход" 220 Y1%=Y% 221 LINE (30%,Y%)-(200%,Y%+20%),1%,B 'Нарисовать рамку темы меню 222 C$=INKEY$ 223 IF C$="" THEN 222 ELSE T%=ASC(C$) 'Между кавычками нет пробела 224 IF T%=26% THEN IF Y%>43% THEN Y%=Y%-20% ELSE Y%=163% 225 IF T%=27% THEN IF Y%<163% THEN Y%=Y%+20% ELSE Y%=43% 226 IF T%=10% THEN IF Y%=163% THEN STOP ELSE 230 227 IF Y%<>Y1% THEN LINE (30%,Y1%)-(200%,Y1%+20%),0%,B 'Стереть рамку темы меню 228 GOTO 220 230 LOCATE 1%,1%,1% 'Восстановить курсор 240 ON (Y%-23%)/20% GOSUB 6000,4000,5000,3000 250 IF Y%=123% THEN 310 ELSE IF Y%<>143% GOTO 40 260 CO%=1% 270 GOSUB 4000 280 ?,, "Для возврата в меню-<ввод>" 'Обратите внимание на запятые 290 IF INKEY$="" THEN 290 ELSE 40 'Между кавычками нет пробела 310 IF KF%=0% THEN SI%=1% ELSE SI%=0% 320 GOTO 40 1000 T%=1% 1010 E%=0% 1020 W%=0% 1030 IF LEN(A$)-1% GOTO 1200 1040 Y$=""+MID$(A$,T%,1%)+" " 'В первых кавычках пробела нет 1050 U%=VAL(Y$) 1060 C%=0% 1070 IF U%<>0% THEN C%=U% 1080 IF ASC(Y$)>70 OR U%<>0% OR Y$="О" GOTO 1140 1090 C%=ASC(Y$)-55% 1140 IF T%>1% GOTO 1190 1150 W%=C% 1170 T%=2% 1180 GOTO 1040 1190 E%=W%*16%+C% 1200 RETURN 3000 I%=KF% 3010 CLS 3020 ? "Перемещение:←,→,↓,↑; Замена: С Выход: Q" 3040 GOSUB 7000 3050 X%=0% 3060 K$=INKEY$ 3070 IF K$="" THEN 3060 ELSE R%=ASC(K$) 'Между кавычками пробела нет 3080 IF FIX(SQR(R%))=5% THEN ON R%-24% GOTO 3210,3170,3190 3090 IF R%=81% THEN 3360 ELSE IF R%=67% THEN 3140 ELSE IF R%=8% THEN 3270 ELSE 3060 3140 GOSUB 8000 3150 D%(I%-KF%,X%)=E% 3160 GOTO 3340 3170 I%=I%-1% 3180 IF I%<KF% THEN I%=KF% ELSE 3340 3185 GOTO 3060 3190 I%=I%+1% 3200 IF I%>KF%+63% THEN I%=KF%+63% ELSE 3340 3205 GOTO 3060 3210 J%=J%+3% 3220 X%=X%+1% 3230 IF J%<= 51% THEN 3260 3240 J%=51% 3250 X%=15% 3260 GOTO 3320 3270 J%=J%-3% 3280 X%=X%-1% 3290 IF J%>=6% THEN 3320 3300 J%=6% 3310 X%=0% 3320 LOCATE J%,5% 3330 GOTO 3060 3340 ? CHR$(155%) 3350 GOTO 3010 3360 ? CHR$(155%) 3370 RETURN 4000 CLS 4005 ? "ЖДИТЕ..." 4007 K%=KF%*16%+&H8000 4010 FOR I%=0% TO 63% 4015 L%=I%*16%+K% 4020 FOR J%=0% TO 15% 4030 N%=L%+J% 4040 POKE &HFFCC,N% 4050 IF CO%=1% GOTO 4100 4060 D%(I%,J%)=PEEK(&HFFCC) 4070 NEXT J%,I% 4090 RETURN 4100 IF D%(I%,J%)=PEEK(&HFFCC) GOTO 4070 4140 ? "ОШИБКА ПО АДРЕСУ ";HEX$(I%+KF%);HEX$(J%);"H" 'После "у" пробел 4160 GOTO 4070 5000 ? "ЖДИТЕ..." 5005 K%=KF%*16%+2048% 5010 FOR I%=0% ТО 63% 5015 L%=I%*16%+K% 5020 FOR J%=0% ТО 15% 5030 N%=L%+J% 5040 FOR W%=0% ТО 5% 5050 POKE &HFFCC,&H8800 5060 POKE &HFFCC,N%+&H8000 5070 PORE &HFFCC,N% 5080 POKE &HFFCC,D%(I%,J%)+30720% 5090 FOR T=0 TO 50 5100 NEXT T,W%,J%,I% 5110 BEEP 5120 RETURN 6000 FOR I%=KF% TO 63%+KF% 6010 CLS 6020 ? "ВЫХОД: Q" 6030 GOSUB 7000 6050 GOSUB 8000 6070 IF A$="Q" GOTO 3360 6075 GOSUB 1000 6077 IF LEN(A$)=1% GOTO 6090 6080 D%(I%-KF%,(J%-6%)/3%)=E% 6090 J%=J%+3% 6100 IF J%>51% THEN 6110 ELSE 6050 6110 ? CHR$(155%) 6130 NEXT I% 6135 J%=6% 6140 RETURN 7000 ? AT(0%,5%);HEX$(I%);CHR$(155%) 7010 FOR J%=0% TO 15% 7020 ? AT(J%*3%+6%,3%);HEX$(J%) 'Номер байта 7030 ? AT(J%*3%+6%,5%);HEX$(D%(I%-KF%) AND &HFF) 7040 NEXT J% 7050 J%=6% 7060 LOCATE 6%,5%,1% 7070 RETURN 8000 LOCATE J%-1%,5% 8010 INPUT A$ 8020 ? AT(J%-1%,5%); " " 'Между кавычками пробел 8030 GOSUB 1000 8040 RETURN