// Пример оформления программы // Работа N 0 // Измерение времени выполнения фрагмента программы // Студенты гр 000.0 Иванофф, Петрофф, & Сидорофф // #include #include #include #include #define PortCan0 0x40 void beep(unsigned iTone,unsigned iDlit); // Это прототип функции // звукового сигнала, используемой при измерении времени void main(void) { // Объявление переменных long int lCnt=0; // Ячейка - счетчик повторений int iA=0x1234; // Думми-ячейка, используемая в исследуемой команде /*************************************************************************\ /* Как из Си посмотреть содержимое байта с известным физическим адресом */ // Если хотим напечатать содержимое байта с адресом 0046Сh //объявляем far-указатель на переменную типа char и инициализируем //этот указатель значением адреса, предварительно преобразовав //его к типу char * char far * pT=(char *)0x46C; // (1) printf ("\n Печатаем 10 раз значение байта с известным адресом \n"); for (int i=0; i<10; i++) printf (" \n %d ", * pT); // (1) printf ("\n Для продолжения нажмите любую клавишу \n"); getch(); // Программа ждет нажатия клавиши /******************************************\ \* Как из Си посмотреть содержимое порта */ // Читаем содержимое порта с адресом 40 с помощью функции Си printf ("\n Читаем содержимое порта с адресом 40 с помощью функции Си \n" ); // Цикл повторяется каждые 0.5 с printf ("\n Для выхода из цикла - нажмите любую клавишу \n" ); while (bioskey(1)==0) // пока не будет нажата любая клавиша { printf (" \n Порт40 = %d ", inp(PortCan0)); //(2) // С помощью TD посмотрите, во что превращается ф-ция inp() // на уровне машинных команд delay(500); // Задержка на полсекунды (500 мс) } getch(); // Очищаем буфер клавиатуры /**************************************************************************\ Примечания: * Функция printf (...) позволяет распечатать на экране значения переменных, * а также произвольный текст. * Функция bioskey(1) позволяет определить, нажата ли клавиша * Функция inp(uPort) позволяет считать байт из порта Port * Функция outp(uPort,iValue) позволяет вывести величину iValue в порт uPort * Функция delay(uTime) организует программную задержку на uTime миллисекунд * Функция getch() считывает один символ из буфера клавиатуры. * В данном случае это надо для очистки буфера клавиатуры * \**************************************************************************/ // Снова читаем тот же порт с помощью программы на встроенном ассемблере printf ("\n Читаем содержимое порта с адресом 40 ассемблером \n" ); while (bioskey(1) == 0 ) // Этот цикл будет повторяться, // пока не нажмем клавишу // Примеры использования встроенного ассемблера (3) { asm { push ax // Один способ записи на встроенном ассемблере in al,0x40 } unsigned char Tmm = _AL; // Эта команда эквивалентна mov Tmm,al // !! Убедитесь в этом с помощью TD asm pop ax // Другой способ записи на встроенном ассемблере delay (500); printf (" \n Порт40 = %d ", Tmm ); // Если нажата клавиша - то выход } getch(); printf ("\n Для продолжения - нажмите любую клавишу \n "); getch(); /************************************************************* Как посмотреть содержимое длинной (например)четырехбайтовой переменной с адреса 0046С c помощью средств Си */ long far * pTime=(long *)0x46C; // Указатель на счетчик тиков while (bioskey(1) == 0) { printf ("\n %ld",*pTime); delay(1000); } getch(); // Читаем и печатаем содержимое двухбайтовой переменной // с адреса 0046C средствами встроенного ассемблера int Time; while (bioskey(1) == 0) { asm push ds // Сохраним на всякий случай регистры asm push si // Во встроенном ассемблере asm mov ax,40h // можно записывать hex-константы так ... asm mov ds,ax asm mov si,0x6C // ... или так asm mov ax,[ds:si] asm mov Time,ax asm pop si // А теперь восстановим регистры asm pop ds // (не перепутайте порядок !!!) printf ("\n %d",Time); delay(300); } /**************************************************************** Пример выполнения задания типа И Требуется измерить время выполнения заданной команды (в примере - команда mov reg,mem, а Вы спросите у преподавателя какую команду Вам взять ) Измерение времени выполнения фрагмента программы */ beep(400,200); // Сигнал отмечает начало интервала (5) for ( lCnt=0; lCnt<1000000; lCnt++) { a1: asm { mov ax,iA mov ax,iA mov ax,iA mov ax,iA mov ax,iA mov ax,iA mov ax,iA mov ax,iA mov ax,iA a2: mov ax,iA } } beep(400,200); // Сигнал отмечает конец интервала (5) } // Функция подачи звукового сигнала заданной высоты и длительности (5) void beep(unsigned iTone,unsigned iDlit) { sound(iTone); delay(iDlit); nosound(); } // Результаты выполнения // Запустили программу дважды // При первом пуске участок между метками a1 и a2 был закомментирован // Время выполнения составило 3.15 +- 0.2 секунд (для оценки ошибки // прогон был повторен 5 раз и было оценено среднее значение и // среднеквадратическое отклонение. Приведенный допуск равен 2*СКО) // При втором пуске участок a1-a2 работал. Время выполнения // составило 7.5 +- 0.2 секунд. Количество повторений команды MOV REG,MEM // равно 10^7, добавка времени равна 4.35 +- 0.3 секунд, оценка времени // выполнения команды 0.44 +- 0.03 мкс. // С помощью TD определили, что компилятор использовал адресацию // базовая со смещением [BP+disp] /* Задание на выполнение работы LAB0-0 1. Прочитайте текст программы и выделите логически связные части, поймите, что делают эти части. Разбирая текст, обратите внимание на то, какие функции Си используются и зачем. Делая это, научитесь пользоваться контекстным Help'ом 2. Скомпилируйте программу и убедитесь, что в ней отсутствуют ошибки 3. Запустите программу, наблюдайте результат и убедитесь, что Вы пони- маете, что происходит. 4. Научитесь, используя встроенный отладчик IDE: Выполнить программу до заданной точки останова. На уровне исходного Си-текста это можно сделать, используя пункты меню Run и Debug/Breakpoints. Проверить/изменить содержимое переменных после останова. Используйте пункт меню Debug/Evaluate/Modify, Debug/Inspect (горячая клавиша Alt/F4) либо Debug/Watches Измерить время выполнения заданной преподавателем команды так, как это сделано в программе LAB0 с командой MOV REG,MEM. 5. Средствами отладчика TD в окне CPU (пункт меню View/CPU) научитесь: - наблюдать/изменять содержимое регистров процессора (подумайте, ка- кие регистры можно менять безболезнено (изменения в некоторых ре- гистрах могут привести к фатальным для нормального выполнения программы результатам) - наблюдать/изменять ячейку памяти с известным физическим адресом: (Прежде, чем писать куда-либо, хорошо было бы подумать: что это за адрес - адрес ОЗУ(не использует ли этот адрес еще какая-нибудь программа), адрес ПЗУ, существует ли физическое устройство, соот- ветствующее данному адресу... Варианты: 1. 0x46C 0x80000 0xF000:0xFFF0 2. 0x0040:0x6D 0x8000:0x0010 0xFFFFE 3. 0x41A 0x80210 0xFFFF:0x100 4. 0x0040:0x1E 0xD00000 0x8000:0x200 5. область памяти - 0x41A-0x43C 0xD000:0x100 0xFFFF:0xE - читать/писать в произвольный порт ввода/вывода (например: считать порт 0x40 несколько раз - попробуйте объяснить наблюдаемый резуль- тат; записать число 3 в 61 порт, потом быстро записать туда нуль); 6. Определите в какие команды Ассемблера оттранслирован с языка СИ оператор цикла. Напишите на уровне Ассемблера свой вариант цикла. Сравните, результаты представте преподавателю. */