Система программирования Turbo Pascal

           

Программирование клавиатуры


Дополнительные возможности управления клавиатурой реализуются двумя функциями: KeyPressed и ReadKey.

Функция KeyPressed.

Возвращает значение типа Boolean, указывающее состояние буфера клавиатуры: False означает, что буфер пуст, a True - что в буфере есть хотя бы один символ, еще не прочитанный программой.

В MS-DOS реализуется так называемый асинхронный буферизованный ввод с клавиатуры. По мере нажатия на клавиши соответствующие коды помещаются в особый буфер, откуда они могут быть затем прочитаны программой. Стандартная длина буфера рассчитана на хранение до 16 кодов символов. Если программа достаточно долго не обращается к клавиатуре, а пользователь нажимает клавиши, буфер может оказаться переполненным. В этот момент раздается звуковой сигнал и «лишние» коды теряются. Чтение из буфера обеспечивается процедурами Read/ReadLn и функцией ReadKey. Замечу, что обращение к функции KeyPressed не задерживает исполнения программы: функция немедленно анализирует буфер и возвращает то или иное значение, не дожидаясь нажатия клавиши.

Функция ReadKey.



Возвращает значение типа Char. При обращении к этой функции анализируется буфер клавиатуры: если в нем есть хотя бы один не прочитанный символ, код этого символа берется из буфера и возвращается в качестве значения функции, в противном случае функция будет ожидать нажатия на любую клавишу. Ввод символа с помощью этой функции не сопровождается эхо-повтором и содержимое экрана не меняется.

Пусть, например, в какой-то точке программы необходимо игнорировать все ранее нажатые клавиши, коды которых еще не прочитаны из буфера, т.е. необходимо очистить буфер. Этого можно достичь следующим способом:

Uses CRT;

var

С: Char; 

begin

while KeyPressed do 

С := ReadKey;

.......

end.

При использовании процедуры ReadKey необходимо учесть, что в клавиатурный буфер помещаются так называемые расширенные коды нажатых клавиш. Если нажимается любая алфавитно-цифровая клавиша, расширенный код совпадает с ASCII-кодом соответствующего символа. Например, если нажимается клавиша с латинской буквой «а» (в нижнем регистре), функция ReadKey возвращает значение chr (97), а если «А» (в верхнем регистре) - значение chr (65). При нажатии функциональных клавиш F1...F10, клавиш управления курсором, клавиш Ins, Home, Del, End, PgUp, PgDn в буфер помещается двухбайтная последовательность: сначала символ #0, а затем расширенный код клавиши. Таким образом, значение #0, возвращаемое функцией ReadKey, используется исключительно для того, чтобы указать программе на генерацию расширенного кода. Получив это значение, программа должна еще раз обратиться к функции, чтобы прочитать расширенный код клавиши.

Т.е. код сканирования клавиши. Этот код определяется порядком, в соответствии с которым микропроцессор клавиатуры Intel 8042 периодически опрашивает (сканирует) состояние клавиш.

Следующая простая программа позволит Вам определить расширенный код любой клавиши. Для завершения работы программы нажмите клавишу Esc.

Uses CRT; 

var

С: Char; 

begin 

repeat

С := ReadKey; 

if C<>#0 then

WriteLn(ord(C)) 

else

WriteLnCO1 ,ord(ReadKey) :8)

until C=#27 {27 - расширенный код клавиши Esc} 

end.

Если Вы воспользуетесь этой программой, то обнаружите, что нажатие на некоторые клавиши игнорируется функцией ReadKey. Это прежде всего так называемые сдвиговые клавиши - Shift, Ctrl, Alt. Сдвиговые клавиши в MS-DOS обычно используются для переключения регистров клавиатуры и нажимаются в сочетании с другими клавишами. Именно таким способом, например, различается ввод прописных и строчных букв. Кроме того, функция игнорирует переключающие клавиши Caps Lock, Num. Lock, Scroll Lock, а также «лишние» функциональные клавиши F11 и F12 клавиатуры IBM AT, не имеющие аналога на клавиатуре ранних моделей IBMPC/XT (в этих машинах использовалась 84-клавишная клавиатура, в то время как на IBM AT - 101-клавишная).

В табл. 13.1 приводятся расширенные коды клавиш, возвращаемые функцией ord(ReadKey). Для режима ввода кириллицы приводятся коды, соответствующие альтернативному варианту кодировки.

Таблица 13.1 Расширенные коды клавиш

Код Клавиша или комбинация клавиш Код Клавиша или комбинация клавиш
Первый байт Второй байт Первый байт Второй байт
Алфавитно-цифровые клавиши
8 - Backspace (Забой) 9 - Tab (Табуляция)
13 - Enter 32 - Пробел
33 - ! 34 - "
35 - # 36 - $
37 - % 38 - &
39 - ' 40 - (
41 - ) 42 - *
43 - + 44 - ,
45 - - 46 - .
47 - / 4S...57 - 0...9
58 -   59 - ;
60 - < 61 - =
62 - > 63 - ?
64 - @ 65...90 - A...Z
91 - [ 92 - \
93 - ] 94 - ^
95 -   96 - '
97...122 - a...z 123 - {
124 - l 125 - }
126 - ~ 128...159 - А...Я
160... 175 - а...п 224...239 - р...я

Управляющие клавиши и их сочетания со сдвиговыми

0 3 Ctrl-2 0 15 Shift-Tab
0 16...25 Alt-Q...Alt-Р (верхний ряд букв) 0 30...38 Alt-A...Alt-L (средний ряд букв)
0 44...50 Alt-Z...Alt-М (нижний ряд букв) 0 59...68 F1...F10
0 - 71 Ноте 0 72 Курсор вверх
0 73 PgUp 0 75 Курсор влево
0 77 Курсор вправо 0 79 End
0 80 Курсор вниз 0 81 PgDn
0 82 Ins 0 83 Del
0 84...93 Shift-Fl...Shift-F10 0 94...103 Ctrl-F1... Ctrl-F10
0 104...113 Alt-Fl...Alt-F10 0 114 Ctrl-PrtScr
0 115 Ctrl-курсор влево 0 116 Ctrl-Курсор вправо
0 117 Ctrl-End 0 118 Ctrl-PgDn
0 119 Ctrl-Home 0 120...131 Alt-1. ..Alt-= (верхний ряд клавиш)
0 132 Ctrl-PgUp      
Назад Начало Вперед

Текстовый вывод на экран


Библиотека Turbo Vision способна удовлетворить самым высоким требованиям и я настоятельно рекомендую обращаться к ней при программировании сложных текстовых изображений (меню, окон и т.п.). Тем не менее вполне возможно, что некоторые из читателей захотят использовать значительно более простые, но достаточно эффективные средства модуля CRT, описываемые в этом разделе.

Используемое в ПК устройство визуального отображения информации - дисплей -состоит из двух основных частей: монитора, содержащего экран (электронно-лучевую трубку или жидкокристаллическую панель) с необходимыми компонентами (устройствами развертки изображения), и блока управления, который чаще называют дисплейным адаптером или просто адаптером. Обычно оба устройства согласуются друг с другом, но в отдельных случаях этого согласования может не быть (например, цветной монитор может работать с монохромным адаптером и наоборот). Будем считать оба устройства согласованными, поэтому, говоря о различных дисплеях, я буду говорить только о различных адаптерах, так как именно в них сосредоточены основные отличия дисплеев друг от друга.

Исторически первым адаптером (1981 г.), использованным на IBM PC, был так называемый монохромный адаптер (MDA). Его возможности очень скромны: он позволял выводить только текстовые сообщения в одном из двух форматов - 25 строк по 40 или по 80 символов в строке. Символы выводились в прямом изображении (светлые символы на темном фоне), причем их ширина оставалась одинаковой в обоих режимах, поэтому при выводе в режиме 40x25 использовалась только левая половина экрана. В MDA применялись два символьных шрифта - обычный и с подчеркиванием.

В 1982 году фирма Hercules выпустила адаптер HGC (от англ. Hercules Graphics Card - графическая карта Геркулес), который полностью эмулировал MDA в текстовом режиме, но в отличие от него мог еще воспроизводить и графические изображения с разрешением 720x350 точек (пикселей).

Примерно в это же время IBM выпустила цветной графический адаптер CGA (Color Graphics Adapter) и впервые на экране ПК появился цвет. CGA позволял выводить как текстовые сообщения, так и графические изображения (с разрешением 320x200 или 640x200 пикселей). В текстовом режиме выводились 40x25 или 80x25 символов как в монохромном, так и в цветном изображениях. При использовании монохромного режима символы, в отличие от MDA, не могли подчеркиваться, зато их можно было

выводить в негативном изображении (черные символы на светлом фоне). При выводе в цветном режиме использовалось 16 цветов для символов и 8 - для окружающего их фона.

Текстовые возможности CGA стали стандартом де-факто и поддерживаются во всех последующих разработках IBM - адаптерах EGA, MCGA, VGA и SVGA. Возможности модуля CRT рассматриваются применительно к адаптерам этого типа.

Процедура TextMode.

Используется для задания одного из возможных текстовых режимов работы адаптера. Заголовок процедуры:

Procedure TextMode(Mode: Word);

Здесь Mode - код текстового режима. В качестве значения этого выражения могут использоваться следующие константы, определенные в модуле CRT:

const

BW40=0{Черно-белый режим 40x25} 

Со40=1{Цветной режим 40x25}

BW80=2{Черно-белый режим 80x25}

Со80=3{Цветной режим 80x25} 

Mono=7{Используется с MDA}

Font8x8=256{Используется для загружаемого шрифта в режиме 80х43

или 80х50 с адаптерами EGA илиVGA}

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

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

Uses CRT;

Procedure Print(S: String);

(Выводит сообщение S и ждет инициативы пользователя} 

begin

WriteLn(S); {Выводим сообщение}

WriteLn('Нажмите клавишу Enter...');

ReadLn {Ждем нажатия клавиши Enter} 

end; {Print}

var

LM: Word;{Начальный режим экрана} 

begin

LM := LastMode; {Запоминаем начальный режим работы дисплея}

TextMode(Со40);

Print('Режим 40x25");

TextMode(CoSO) ;

Print('Режим 80x25');

TextMode(Co40+Font8x8);

Print('Режим Co40+Font8x8') ;

TextMode(Co80+Font8x8);

Print('Режим Co80+Font8x8'); 

{Восстанавливаем исходный режим работы:} 

TextMode(LM) 

end.

Процедура TextColpr.

Определяет цвет выводимых символов. Заголовок процедуры:

Procedure TextColor(Color: Byte);

Процедура TextBackground.

Определяет цвет фона. Заголовок: 

Procedure TextBackground(Color: Byte);

Единственным параметром обращения к этим процедурам должно быть выражение типа Byte, задающее код нужного цвета. Этот код удобно определять с помощью следующих мнемонических констант, объявленных в модуле CRT:

const

Black = 0;{Черный}

Blue = 1;{Темно-синий}

Green = 2 ;{Темно-зеленый}

Cyan = 3;{Бирюзовый}

Red = 4 ;{Красный}

Magenta = 5;{Фиолетовый}

Brown = 6 ;{Коричневый}

LightGray = 7;{Светло-серый}

DarkGray = 8;{Темно-серый}

LightBlue = 9;{Синий}

LightGreen = 10;{Светло-зеленый}

LightCyan = 11;{Светло-бирюзовый}

LightRed = 12;{Розовый}

LightMagenta = 13;{Малиновый}

Yellow = 14;{Желтый}

White ' =15;{Белый}

Blink =128;{Мерцание символа}

Следующая программа иллюстрирует цветовые возможности Турбо Паскаля.

Uses CRT; 

const

Col: array [1..15] of String [16] =

('темно-синий','темно-зеленый','бирюзовый','красный',

'фиолетовый','коричневый','светло-серый','темно-серый',

'синий','зеленый','светло-бирюзовый','розовый',

'малиновый','желтый','белый'); 

var

k: Byte; 

begin

for k := 1 to 15 do

begin {Выводим 15 сообщений различными цветами}

TextColor(k);

WriteLn('Цвет ', k, ' - ',Col[k]) 

end;

TextColor(White+Blink); {Белые мигающие символы} 

WriteLn('Мерцание символов'); 

{Восстанавливаем стандартный цвет} 

TextColor(LightGray); 

WriteLn 

end.

Обратите внимание на последний оператор WriteLn: если его убрать, режим мерцания символов сохранится после завершения программы, несмотря на то, что перед ним стоит оператор

TextColor(LightGray)

Дело в том, что все цветовые определения предварительно заносятся в специальную переменную TextAttr модуля CRT и используются для настройки адаптера только при обращении к процедурам Write/WriteLn.

Процедура ClrScr.

Очищает экран или окно (см. ниже процедуру Window). После обращения к ней экран (окно) заполняется цветом фона и курсор устанавливается в его левый верхний угол. Например:

Uses CRT; 

var

С: Char 

begin

TextBackground(red) ;

ClrScr;{Заполняем экран красным цветом}

WriteLn('Нажмите любую клавишу...'); 

С := ReadKey; {Ждем нажатия любой клавиши} 

TextBackground(Black) ;

ClrScr {Восстанавливаем черный фон экрана} 

end.

Процедура Window.

Определяет текстовое окно - область экрана, которая в дальнейшем будет рассматриваться процедурами вывода как весь экран. Сразу после вызова процедуры курсор помещается в левый верхний угол окна, а само окно очищается (заполняется цветом фона). По мере вывода курсор, как обычно, смещается вправо и при достижении правой границы окна переходит на новую строку, а если он к этому моменту находился на последней строке, содержимое окна сдвигается вверх на одну строку, т.е. осуществляется «прокрутка» окна. Заголовок процедуры:

Procedure Window(XI,Y1,X2,Y2: Byte);

ЗдесьX1...Y2 - координаты левого верхнего (XI,Y1) и правого нижнего (X2,Y2) углов окна. Они задаются в координатах экрана, причем левый верхний угол экрана имеет координаты (1,1), горизонтальная координата увеличивается слева направо, а вертикальная - сверху вниз.

В следующем примере иллюстрируется вывод достаточно длинного сообщения в двух разных окнах.

Uses CRT; 

var

k: integer; 

begin

{Создаем левое окно -желтые символы на синем фоне:}

TextBackground(Blue);

Window(5,2,35,17);

TextColor(Yellow);

for k := 1 to 100 do

Write(' Нажмите клавишу Enter...');

ReadLn; {Ждем нажатия Enter}

ClrScr; {Очищаем окно}

{Создаем правое окно - белые символы на красном фоне:}

TextBackground(Red);

TextColor(White);

Window(40,2,70,17);

for k := 1 to 100 do

Write(' Нажмите клавишу Enter...');

ReadLn;

TextMode(C080) {Сбрасываем все установки} 

end.

Обращение к процедуре Window игнорируется, если какая-либо из координат выходит за границы экрана или если нарушается одно из условий: Х2>Х1 и Y2>Y1. Каждое новое обращение к Window отменяет предыдущее определение окна. Границы текущего окна запоминаются в двух глобальных переменных модуля CRT: переменная WindMin типа Word хранит X1 и Y1 (XI - в младшем байте), а переменная того же типа WindMax - Х2 и Y2(X2 - в младшем байте). При желании Вы можете изменять их нужным образом без обращения к Window. Например, вместо оператора

Window(40,2,70,17); 

можно было бы использовать два оператора

WindMin := 39+(1 shl 8); 

WindMax := 69+(16 shl 8);

(в отличие от обращения к Window координаты, хранящиеся в переменных WindMin и WindMax, соответствуют началу отсчета 0,0).

Процедура GotoXY.

Переводит курсор в нужное место экрана или текущего окна. Заголовок процедуры:

Procedure GotoXY(X,Y: Byte);

Здесь X, Y - новые координаты курсора. Координаты задаются относительно границ экрана (окна), т.е оператор

GotoXY(1,1);

означает указание перевести курсор в левый верхний угол экрана (или окна, если к этому моменту на экране определено окно). Обращение к процедуре игнорируется, если новые координаты выходят за границы экрана (окна).

Функции whereX и WhereY.

С помощью этих функций типа Byte можно определить текущие координаты курсора: WhereX возвращает его горизонтальную, a WhereY - вертикальную координаты.

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

Uses CRT;

const

LU =#218;{Левый верхний угол рамки} 

RU =#191;{Правый верхний угол)}

LD =#192;{Левый нижний} 

RD =#217;{Правый нижний} 

H =#196;{Горизонтальная черта} 

V =#179;{Вертикальная черта} 

X1 =14;{Координаты окна}

Y1 =5;

X2 =66;

Y2 =20;

Txt = 'Нажмите клавишу Enter...'; 

var

k: integer; 

begin

ClrScr; {Очищаем экран} 

{Создаем окно в центре экрана - желтые символы на синем фоне:}

TextBackground(Blue);

TextColor(Yellow);

Window(X1,Y1,X2,У2);

ClrScr;

{Обводим окно рамкой}

Write(LU); {Левый верхний угол}

{Горизонтальная линия}

for k: = X1+1 to X2-1 do Write(H);

Write(RU);{Верхний правый угол}

for k := Y1+1 to Y2-1 do{Вертикальные линии}

begin

GotoXY(1,k-Y1+1);{Переходим к левой границе}

Write(V);{Левая черта}

GotoXY(X2-X1+1,WhereY){Правая граница}

Write(V){Правая черта} 

end;

Write(LD);

{Левый нижний угол}

Window(X1,Y1,X2,Y2+1);{Расширяем вниз на одну строку координаты окна, иначе вывод в правый нижний угол вызовет прокрутку окна вверх} 

GotoXY(2,Y2-Y1+1); {Возвращаем курсор из левого верхнего угла окна на нужное место}

{Горизонтальная рамка}

for k:= X1+1 to X2-1 do Write(H);

Write(RD); {Правый нижний угол} 

{Определяем внутреннюю часть окна} 

Window(X1+1,Y1+1,X2-1,Y2-1); 

{Выводим левый столбец} 

for k := Y1+1 to Y2-2 do

WriteLn('Левый столбец, строка ',k-Y1);; 

{Ждем нажатия любой клавиши} 

Write('Нажмите любую клавишу...'); 

k := ord(ReadKey); if k=0 then

k := ord(ReadKey);

DelLine; {Стираем приглашение} 

{Выводим правый столбец} 

for k := Y1+1 to Y2-2 do 

begin

GotoXY((X2-X1) div 2,k-Y1); 

Write('Правый столбец, строка ',k-Y1) 

end ;

{Выводим сообщение и ждем нажатия клавиши Enter} 

GotoXY((X2-X1-Length(Txt)) div 2,Y2-Y1-1); 

TextColor(White); 

Write(Txt); 

ReadLn;

{Восстанавливаем стандартный режим} 

TextMode(CO80) 

end.

Три следующие процедуры без параметров могут оказаться полезными при разработке текстовых редакторов.

Процедура ClrEOL.

Стирает часть строки от текущего положения курсора до правой границы окна (экрана). Положение курсора не меняется.

Процедура DelLine.

Уничтожает всю строку с курсором в текущем окне (или на экране, если окно не создано). При этом все строки ниже удаляемой (если они есть) сдвигаются вверх на одну строку.

Процедура InsLine.

Вставляет строку: строка с курсором и все строки ниже ее сдвигаются вниз на одну строку; строка, вышедшая за нижнюю границу окна (экрана), безвозвратно теряется; текущее положение курсора не меняется.

Процедуры LowVideo, NormVideo и HighVideo.

С помощью этих процедур без параметров можно устанавливать соответственно пониженную, нормальную и повышенную яркость символов. Например:

Uses CRT; 

begin

LowVideo;

WriteLn('Пониженная яркость');

NormVideo;

WriteLn('Нормальная яркость'); 

HighVideo;

WriteLn('Повышенная яркость') 

end.

Замечу, что на практике нет разницы между пониженной и нормальной яркостью изображения.

Процедура AssignCRT.

Связывает текстовую файловую переменную F с экраном с помощью непосредственного обращения к видеопамяти (т.е. к памяти, используемой адаптером для создания изображения на экране). В результате вывод в такой текстовый файл осуществляется значительно (в 3...5 раз) быстрее, чем если бы этот файл был связан с экраном стандартной процедурой Assign. Заголовок процедуры:

Procedure AssignCRT(F: Text);

В следующей программе измеряется скорость вывода на экран с помощью стандартной файловой процедуры и с помощью непосредственного обращения к видеопамяти. Вначале файловая переменная F связывается «медленной» процедурой Assign со стандартным устройством CON (т.е. с экраном) и подсчитывается количество N1 циклов вывода некоторого текста за 5*55 = 275 миллисекунд системных часов. Затем файловая переменная связывается с экраном с помощью процедуры быстрого доступа AssignCRT и точно так же подсчитывается количество N2 циклов вывода. В конце программы счетчики N1 и N2 выводятся на экран.

Замечу, что показания системных часов хранятся в оперативной памяти компьютера в виде четырехбайтного слова по адресу [$0040:$006С] и наращиваются на единицу каждые 55 миллисекунд.

Uses CRT; 

var

F: Text;

t: LongInt;{Начало отсчета времени}

N1,N2: Word;{Счетчики вывода}

const

txt = ' Text'; 

begin

{----- Стандартный вывод в файл -----}

Assign(F,'CON');

Rewrite(F);

N1 := 0;{Готовим счетчик вывода}

ClrScr;{Очищаем экран}

{Запоминаем начальный момент:}

t := MemL[$0040:$006C];

{Ждем начала нового 55-мс интервала, чтобы исключить погрешность в определении времени:}

while MemL[$0040:$006C]=t do;

{Цикл вывода за 5 интервалов}

while MemL[$0040:$006С]<t+6 do

begin

inc(N1) ; 

Write(F,txt) 

end;

Close(F);

{----- Вывод с помощью быстрой процедуры прямого доступа к экрану - ----} 

AssignCRT(F); 

Rewrite(F); 

N2 := 0;

ClrScr; 

t := MemL[$0040:$006C]; 

while MemL[$0040:$006C]=t do; 

while MemL[$0040:$006C]<t+6 do 

begin

inc(N2); 

Write(F,txt) 

end ;

Close(F);

{Печатаем результат} 

ClrScr;

WriteLn(Nl,N2:10) 

end.

Следует учесть, что вывод на экран обычным образом - без использования файловой переменной (например, оператором Write (txt)) также осуществляется с помощью непосредственного доступа к видеопамяти, поэтому ценность процедуры AssignCRT весьма сомнительна. Прямой доступ к видеопамяти регулируется глобальной логической переменной DirectVideo модуля CRT: если эта переменная имеет значение True, доступ разрешен, если False - доступ к экрану осуществляется с помощью относительно медленных средств операционной системы MS-DOS. По умолчанию переменная DirectVideo имеет значение True.


Программирование звукового генератора


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

В модуль CRT включены три процедуры, с помощью которых Вы сможете запрограммировать произвольную последовательность звуков.

Процедура Sound.

Заставляет динамик звучать с нужной частотой. Заголовок процедуры:

Procedure Sound(F: Word);

Здесь F - выражение типа Word, определяющее частоту звука в герцах. После обращения к процедуре включается динамик и управление немедленно возвращается в основную программу, в то время как динамик будет звучать впредь до вызова процедуры NoSound.

Процедура No Sound.

 Выключает динамик. Если он к этому моменту не был включен, вызов процедуры игнорируется.

Процедура Delay.

Обеспечивает задержку работы программы на заданный интервал времени. Заголовок процедуры:

Procedure Delay(T: Word);

Здесь Т - выражение типа Word, определяющее интервал времени (в миллисекундах), в течение которого задерживается выполнение следующего оператора программы.

Для генерации звукового сигнала обычно используется вызов описанных процедур по схеме Sound-Delay-NoSound. Следующая программа заставит ПК воспроизвести простую музыкальную гамму. Используемый в ней массив F содержит частоты всех полутонов в первой октаве от «до» до «си». При переходе от одной октавы к соседней частоты изменяются в два раза.

Uses CRT; 

const

F: array [1..12] of Real =

(130.8, 138.6, 146.8, 155.6, 164.8, 174.6, 185.0, 196.0, 207.7, 220.0,

233.1, 246.9);{Массив частот 1-й октавы}

Temp = 100;{Темп исполнения} 

var

k,n: Integer; 

begin

{Восходящая гамма} 

for k := 0 to 3 do for n := 1 to 12 do 

begin

Sound(Round(F[n]*(1 shl k) )) ; 

Delay(Temp); 

NoSound 

end ;

{Нисходящая гамма} 

for k := 3 downto 0 do 

for n := 12 downto 1 do 

begin

Sound(Round(F[n]*(1 shl k)) ) ; 

Delay(Temp); 

NoSound 

end

end.