Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекції_СПр.docx
Скачиваний:
37
Добавлен:
21.08.2019
Размер:
947.09 Кб
Скачать
  1. Таймер bios.

  2. Керування пам’яттю за допомогою функцій biosmemory.

  3. Робота з пам’яттю в С++ та Асемблер.

Навчальна мета: Засвоїти основні поняття застосування таймерів, керування пам’яттю за допомогою функцій biosmemory.

Виховна мета: Розуміння роботи з пам’яттю в С++ та Асемблер допомагає студентам зрозуміти суть компоновки програм.

Актуальність: Керування пам’яттю за допомогою функцій biosmemory завжди було основою розуміння системного програмування.

Мотивація: Мотивацією вивчати даний напрямок у курсі системного програмування є те, що розуміння роботи з пам’яттю допоможе студентам у виконанні курсового проекту.

Функція Повертає размір оперативної пам’яті.

Синтаксис #include<bios.h>

int biosmemory(void);

Файл,що містить прототип bios.h

Опис biosmemory за допомогою переривання BIOS 0x12 отримує значення, рівне розміру оперативної пам’яті. Це значення не містить відео-пам’ять, розширену пам’ять чи додаткову пам’ять.

Повертаєме значення biosmemory повертає размір оперативної пам’яті в блоках по 1К.

Переносимість biosmemory підтримується тільки на комп’ютерах IBM PC чи сумісних з ними.

Приклад:

#include<stdio.h>

#include<bios.h>

int main(void)

{

int memory_size;

memory_size = biosmemory(); /* повертає до 640К */

printf("Оперативная пам’ять: %dK\n",memory_size);

return 0;

}

Робота з пам’яттю в С++ та Асемблер

Для роботи з оперативною пам‘яттю в асемблері використовуються спеціальні функції переривання.

Для запиту обсягу фізичної пам‘яті можна скористатись перериванням 12H, яке повертає в регістрі AX розмір пам'яті в кілобайтах, наприклад, шістнадцяткове 200 відповідає пам'яті в 512 Кбайт. Слід пам‘ятати, що йдеться тільки про сегмент реальної, а не розширеної (extended) пам‘яті.

А для виділення фрагментів динамічної пам‘яті застосовується переривання 21h.

Функція 48h (заноситься в регістр AH) відповідає за виділення фрагменту динамічної пам‘яті. Обсяг вираховується в параграфах (16-байтних комірках, що відповідає так званому «double quad word», найбільшому цілочисельному типу даних в сучасних мікропроцесорах) і заноситься в регістр BX. В тому ж регістрі повертається і обсяг доступної пам‘яті (у випадку помилки). Одним із поширених методів визначення обсягу доступної пам‘яті є виділення максимальної кількості параграфів (шістнадцяткове FFFF), і на виході функції 48h буде точне значення поточного розміру динамічної пам‘яті, доступної програмі. При помилці встановлюється прапорець CF, а в регістр AX заноситься код помилки. Якщо ж виділення пам‘яті пройшло успішно, в регістрі AX опиниться адреса початку виділеного фрагменту (знову ж таки, в одиницях параграфів). Ця ж функція може бути використана для доступу до вищих блоків пам‘яті, але ця процедура буде описана трохи нижче.

Наступною функцією роботи із пам‘яттю є 49h. Вона є прямою протилежністю попередній, і звільняє виділену попередньо пам‘ять. Для цього треба просто занести адресу сегменту в регістр ES, і викликати переривання 21h. Система вивільнить вказаний фрагмент пам‘яті і він знову стане частиною вільної динамічної пам‘яті. Слід зазначити, що ця функція здатна працювати лише із тими частинами пам‘яті, які виділені поточному процесу (стосується Windows). Вивільнити пам‘ять, яку розподілила інша програма, можна тільки «прикинувшись» іншим процесом (це дозволяє зробити недокументована функція 50h).

Нарешті, функція 4ah дозволяє оперативно перерозподілити (тобто зменшити чи збільшити) виділений фрагмент пам‘яті, уникаючи процедури звільнення-повторного виділення. Для цього слід в регістр ES занести адресу потрібного фрагменту, в BX – бажаний новий обсяг. У випадку помилки, як і в функції 48h, буде повернуто в регістрі BX доступний обсяг пам‘яті, а в AX – код помилки.

Вище ми уже говорили, що існує спосіб виділити пам‘ять і у вищих блоках пам‘яті. Для цього нам знадобиться кілька додаткових функцій і особлива послідовність дій (у всіх діях застосовуються виключно функції 21h).

  • За допомогою функцій 5800h та 5802h слід отримати поточні установки доступу до пам‘яті і зберегти їх (наприклад, в стеку). В першому випадку зберігати треба вміст регістру AX (він міститиме код поточної стратегії виділення пам‘яті), в другому – регістру AL (він міститиме статус підключення вищих блоків пам‘яті).

  • За допомогою функції 5803h встановлюється зв‘язок із розширеною пам‘яттю. Для цього в регістр BX заносимо 01h. Уважно слідкуйте за результатом цієї функції: якщо вона видасть помилку, на що вкаже прапорець CF, подальші кроки попросту не мають сенсу.

  • За допомогою функції 5801h встановлюємо стратегію виділення пам‘яті тільки у верхніх блоках. Для цього в регістр BX заносимо значення 0041h (стратегія виділення лише у верхній пам‘яті, із аналізом найкращого вільного блоку).

Все. Після цього функція 48h буде працювати із розширеною пам‘яттю. Але реальний блок при цьому використовувати не можна. Слід повернути все на свої місця, для чого визначити стратегію виділення як 0001h (стратегія виділення у реальному сегменті), а також зняти прапорець зв‘язку із вищими блоками (00h в BX при виклику функції 5803h) і відновити байти стану, які були збережені раніше.

На фоні складної і громіздкої роботи із перериваннями в Ассемблері, C++ вигідно відрізняється зручною роботою з пам'яттю. Саме тому С++ частіше використовується для створення системного програмного забезпечення.

Розберемо деякі оператори для роботи з пам'яттю.

Оператор malloc

Виділяє блок пам'яті. Розмір блоку задається в байтах. Повертає адреса виділеного блоку або нуль, якщо не вдалося виділити пам'ять. Знаходиться в бібліотеках alloc.h і stdlіb.h.

char *str; // Вказівник на рядок символів

str = malloc(10); // Виділяємо 10 байт, адресу виділеної пам'яті записуємо в str

іf ! ( str )

{

cout << "Не вистачає пам'яті";

exіt (0);

}

strcpy(str, "Hello"); // Копіюємо рядок "Hello" у блок пам'яті з адресою str

malloc часто використовується разом з оператором sіze. За допомогою sіze можна визначити скільки байт виділений під змінну або тип даних.

Free Звільняє пам'ять, виділену оператором malloc.

Приклад.

Звільнимо пам'ять, виділену в попередньому прикладі

free (str);

У версіях починаючи з 3- й Borland C++ містять оператори new і delete. Вони є поліпшеними варіантом операторів malloc і free.

new дозволяє виділяти пам'ять, необхідну для розміщення змінної, структурної змінної або масиву. Для змінної необхідно вказувати тип і у випадку масиву - размер. new може записувати значення в створену змінну.

У випадку успішного з new повертає адреса створеної змінної.

На відміну від malloc, при використанні оператора new непотрібно вказувати кількість байт пам'яті, який необхідно виділити.

Приклади.

/* Опишемо з на цілий і речовинну змінні й з на символ */

Іnt * uk_і;

double * uk_d;

char * uk_c;

/* Виділимо пам'ять під число типу іnt адреса пам'яті з вказівнику uk_і */

uk_і = new іnt;

* uk_і=10; // запишемо число 10 у комірку пам'яті з адресою uk_і;

/* Виділимо пам'ять під число типу double, запишемо в неї число 3.1415 і

привласнимо вказівнику uk_d її адреса */

uk_d=new double (3.1415);

/* Виділимо пам'ять під число

uk_з=new char [ 60 ] ;

Можна виконати перевірку:

Іf !(uk_і && uk_d && uk_c)

{

cout << "Не вистачає пам'яті";

}

Оператор delete Звільняє пам'ять, виділену оператором new.

Приклади.

Продовжимо попередній приклад, звільнимо виділену пам'ять.

delete uk_і;

delete uk_d;

delete [ kol_s ] uk_s;

де kol_s - кількість символів у рядку.

Виклики BIOS використовують програмні переривання. BIOS має трохи різних переривань для різних цілей. Одне з них ми будемо використовувати для доступу до екрана. Це переривання 16 (10Н), що використовується для доступу до дисплея. Як і багато переривань BIOS, переривання 16 має кілька режимів, вибір яких виконується за значенням регістра AH. Якщо функція повертає значення, то воно заноситься в регістр AL. Однак, іноді для повернення декількох значень використовуються інші регістри. Для доступу до переривань BIOS вам доведеться використовувати функцію С int86(). (Деякі компілятори можуть називати цю функцію іншим ім'ям, але MicroSoft C і Турбо С називають її int86().

Функція int86() має наступну форму:

int int86(num,inregs,outregs)

int num; /* номер переривання */

union REGS *inregs; /* вхідні значення регістрів */

union REGS *outregs; /* вихідні значення регістрів */

Функція int86() повертає значення регістра АХ. Тип REGS описується в заголовку DOS.H. Цей тип показаний тут так, як він визначений у Турбо С, однак, він аналогічно визначений у MisroSoft C і в інших компіляторах.

struct WORDREGS {

unsigned int ax, bx, cx, dx, si, di, cflag, flags;

};

struct BYTEREGS {

unsigned char al, ah, bl, bh, cl, ch, dl, dh;

};

union REGS {

struct WORDREGS x;

struct BYTEREGS h;

};

Як ви можете бачити, REGS - це об'єднання двох структур. Використання структури WORDREGS дозволяє розглядати регістри ЦП як 16-бітні числа. BYTREGS дає вам доступ до окремих 8- бітним регістрам. Наприклад, для доступу до переривання 16, функції 5, ви повинні використовувати наступну послідовність.

union REGS in,out;

in.h.ah=5;

int86(16,&in,&out);

Для збереження вмісту екрана, повинно бути прочитано і занесено в пам’ять поточне значення кожної позиції екрана. Для зчитування символу з визначеної позиції екрана, використовується переривання 16, функція 8, що повертає символ і зв'язаний з ним аттрибут поточної позиції курсору. Для зчитування символу з визначеного місця екрана, ви повинні мати спосіб встановлення курсору. Хоча деякі компілятори С підтримують цю функцію, багато хто її не мають. Проте показана нижче функція goto_xy() може бути використана. Вона використовує переривання 16, функцію 2 з координатою стовпця в DL і координатою ряду в DH. Відеосторінка задається у ВН (використовується сторінка 0 за замовчуванням).

/* встановлення курсору в x,y */

void goto_xy(x,y)

int x,y;

{

union REGS r;

r.h.ah=2; /* функція встановлення курсору */

r.h.dl=y; /* координата стовпчика */

r.h.dh=x; /* координата рядка */

r.h.bh=0; /* відео сторінка */

int86(0x10,&r,&r);

}

Переривання 16, функція 8 повертає символ з поточної позиції курсору в AL і його атрибут у AH.

Функція save_video(), показана тут, зчитує частина екрана, зберігає інформацію в буфер, і очищає цю частину екрана.

/* збереження частини екрана */

void save_video(startx,endx,starty,endy,buf_ptr)

int startx,endx,starty,endy;

unsigned int *buf_ptr;

{

union REGS r;

register int i,j;

for(i=starty;i<endy;i++)

for(j=startx;j<endx;j++) {

goto_xy(j,i);

r.h.ah=8; /* функція читання символу */

r.h.bh=0; /* відео сторінка */

*buf_ptr++ = int86(0x10,&r,&r);

putchar(' '); /* очищення екрана */

}

}

Перші чотири параметри save_video визначають координати верхнього лівого і правого нижнього кута області, що зберігається. Параметр buf_ptr це цілий покажчик на область пам'яті, що містить інформацію. Вона повинна бути досить великий, щоб розмістити всю інформацію, зчитану з екрана.

Відновлення екрана після зроблених змін, полягає просто в записі попередньо зпнесеної в пам’ять інформації назад у відео пам'ять. Для того, щоб зробити це, використовуйте переривання 16, функцію 9, що вимагає, щоб символ був у AL, атрибут у BL, відео сторінка у ВН, а кількість записуваних символів у CX (у нашому випадку 1). Функція restore_video(), описана тут, поміщає інформацію з буфера, на який указує buf_ptr, на екран, заданий початковими і кінцевими координатами X і Y.

/* відновлення частини екрана */

void restore_video(startx,endx,starty,endy,buf_ptr)

int startx,endx,starty,endy;

unsigned int *buf_ptr;

{

union REGS r;

register int i,j;

for(i=starty;i<endy;i++)

for(j=startx;j<endx;j++) {

goto_xy(j,i);

r.h.ah=9; /* функція запису символу */

r.h.bh=0; /* відео сторінка */

r.x.cx=1; /* число повторень символу */

r.h.al=*buf_ptr++; /* символ */

r.h.bl=*buf_ptr++; /* атрибут */

*buf_ptr++ = int86(0x10,&r,&r);

}

}

Файловий доступ

Перш ніж можна зчитувати з деякого чи файлу записувати в нього, цей файл повинен бути відкритий за допомогою функції fopen зі стандартної бібліотеки. Вона проводить деякі обслуговуючі дії і переговори з операційною системою (деталі яких не повинні нас стосуватися) і повертає внутрішнє ім'я, що повинне використовуватися при наступних читаннях з чи файлу записах у нього.

Це внутрішнє ім'я, називане "покажчиком файлу", фактично є покажчиком структури, що містить інформацію про файл, таку як місце розміщення буфера, що тече позиція символу в буфері, чи відбувається читання з чи файлу запис у нього тощо. Користувачі не зобов'язані знати ці деталі, тому що серед визначень для стандартного введення-виведення, одержуваних з файлу stdio.h, Міститься визначення структури з ім'ям file. Єдине необхідне для покажчика файлу опис демонструється прикладом:

file *fopen(), *fp;

тут говориться, що fp є покажчиком на file і fopen повертає покажчик на file. Зверніть увагу, що file є ім'ям типу, подібним int, а не ярлику структури; це реалізовано як typedef.

Фактичне звертання до функції fopen у програмі має вид:

fp=fopen(name,mode);

першим аргументом функції fopen є "ім'я" файлу, що задається у виді символьного рядка. Другий аргумент mode ("режим") також є символьним рядком, що вказує, як цей файл буде використовуватися. Припустимими режимами є: читання ("r"), запис ("w") і додавання ("a").

Якщо ви відкриєте файл, що ще не існує, для чи запису додавання, то такий файл буде створений (якщо це можливо). Відкриття існуючого файлу на запис приводить до відкидання його старого вмісту. Спроба читання неіснуючого файлу є помилкою. Помилки можуть бути обумовлені й інші причини (наприклад, спробою читання з файлу, не маючи на те дозволу). При наявності якої-небудь помилки функція повертає нульове значення покажчика NULL (яке для зручності також визначається у файлі stdio.h).

Іншою необхідною річчю є спосіб чи читання запису, якщо файл уже відкритий. Тут є кілька можливостей, з яких getc і putc є найпростішими.

Функція getc повертає наступний символ з файлу; їй необхідний покажчик файлу, щоб знати, з якого файлу читати. Таким чином,

c=getc(fp)

поміщає в "c" наступний символ з файлу, зазначеного за допомогою fp, і EOF, якщо досягнуть кінець файлу.

Функція putc, що є звертанням до функції getc, putc(c,fp) поміщає символ "c" у файл fp і повертає "c". Подібно функціям getchar і putchar, getc і putc можуть бути макросами, а не функціями.

При запуску програми автоматично відкриваються три файли, що постачені визначеними покажчиками файлів. Цими файлами є стандартне введення, стандартне виведення і стандартне виведення помилок; відповідні покажчики файлів називаються stdin, stdout і stderr. Звичайно всі ці покажчики зв'язані з терміналом, але stdin і stdout можуть бути переспрямовані на чи файли в потік (pipe).

Функції getchar і putchar можуть бути визначені в терміналах getc, putc, stdin і stdout у такий спосіб: #define getchar() getc(stdin) #define putchar(c) putc(c, stdout)

При роботі з файлами для форматного введення і виведення можна використовувати функції fscanf і fprintf. Вони ідентичні функціям scanf і printf, за винятком того, що першим аргументом є покажчик файлу, що визначає той файл, що буде чи читатися куди буде вестися запис; керуюча рядок буде другим аргументом.

Покінчивши з попередніми зауваженнями, ми тепер у стані написати програму cat для конкатенації файлів. Використовувана тут основна схема виявляється зручної в багатьох програмах: якщо маються аргументи в командному рядку, то вони обробляються послідовно. Якщо такі аргументи відсутні, то обробляється стандартне введення. Це дозволяє використовувати програму як самостійно, так і як частина більшої задачі.

#include <stdio.h>

main(argc, argv) /*cat: concatenate files*/

int argc;

char *argv[];

{

file *fp, *fopen();

if(argc==1) /*no args; copy standard input*/

filecopy(stdin);

else

while (--argc > 0)

if ((fp=fopen(*++argv,"r"))==NULL) {

printf("cat:can't open %\n",*argv);

break;

} else {

filecopy(fp);

fclose(fp);

}

}

filecopy(fp) /*copy file fp to standard output*/

file *fp;

{

int c;

while ((c=getc(fp)) !=EOF)

putc(c, stdout);

}

Покажчики файлів stdin і stdout заздалегідь визначені в бібліотеці введення-виведення як стандартне введення і стандартне виведення; вони можуть бути використані в будь-якім місці, де можна використовувати об'ект типу file*.Вони однак є константами, а не змінними, так що не намагайтеся їм що-небудь привласнювати.

Функція fclose є зворотної стосовно fopen; вона розриває зв'язок між покажчиком файлу і зовнішнім ім'ям, встановлений функцією fopen, і вивільняє покажчик файлу для іншого файлу.Більшість операційних систем мають деякі обмеження на число одночасно відкритих файлів, якими може розпоряджатися програма. Тому, те як ми надійшли в cat, звільнивши не потрібні нам більш об'екти, є гарною ідеєю. Мається й інша причина для застосування функції fclose до вихідного файлу - вона викликає видачу інформації з буфера, у якому putc збирає виведення. (При нормальному завершенні роботи програми функція fclose викликається автоматично для кожного відкритого файлу).

bioscom Виконує послідовний введення-виведення

Короткий приклад:

#include <bios.h>

int bioscom(int cmd, char abyte, int port);

bioscom здійснює різні комунікаційні зв'язки відповідно до протоколу обміну інтерфейсу RS-232 через порт введення-виведення, заданий параметром port.

Значення port, рівне 0, відповідає COM1, 1 відповідає COM2 і т.д.

Значення cmd може бути одним з наступних:

Значення

Опис

0

Установлює комунікаційні параметри рівними значенню, зазначеному в abyte.

1

Передає по лінії зв'язку символ у abyte.

2

Приймає символ по лінії зв'язку.

3

Повертає поточний стан комунікаційного порту.

abyte являє собою комбінацію з наступних біт (з кожної групи вибирається одне значення):

Біт

Значення

0х02

7 біт даних

0х03

8 біт даних

0х40

300 бод

0х00

1 стоп-битий

0х04

2 стоп-бита

0х00

немає контролю парності

0х08

контроль непарності

0х18

контроль парності

0х00

110 бод

0х20

150 бод

0х60

600 бод

0х80

1200 бод

0хA0

2400 бод

0хC0

4800 бод

0хE0

9600 бод

Наприклад, значення abyte 0хев (0хе0|0х08|0х00| 0х03) встановлює для комунікаційного порту швидкість передачі 9600 бод, контроль непарності, 1 стоп-битий і 8 біт даних.

Функція bioscom використовує переривання BIOS 0x14.

Для всіх значень cmd bioscom повертає 16- значення бітове ціле, у якому 8 старших біт є бітами стану, а 8 молодших - змінюються в залежності від значення cmd. Старші біти значення, що повертається, визначаються в такий спосіб:

Біт

Значення

15

тайм-аут (минув час)

14

зсуваючий регістр передачі порожній

13

передавальний регістр порожній

12

фіксація переривання

11

помилка синхронізації

10

помилка парності

9

помилка переповнення

8

готовність даних

Якщо значення abyte не змогло бути передане, 15-ий біт встановлюється в 1. У противному випадку ті, що залишилися старші і молодші біти встановлюються відповідним чином. Наприклад, якщо виникла помилка синхронізації, то біт 11 встановлюється в 1.

Якщо параметр cmd має значення 2, то при відсутності помилки байт, що зчитується, записується в молодші біти значення, що повертається. При виявленні помилки в 1 встановлюється принаймні один зі старших біт. Якщо жоден зі старших біт не встановлений у 1, значить байт був прийнятий без помилки.

Якщо параметр cmd має значення 0 чи 3, то старші біти значення, що повертається, встановлені як описано вище, а молодші біти визначаються в такий спосіб:

Біт

Значення

7

отримано сигнал відкриття лінії

6

індикатор кільця

5

готовність набору даних

4

посилка скидання

3

отримано дельта-сигнал відкриття лінії

2

обрив кільця

1

зміна в готовності набору даних

0

дельта-скидання

Приклад

#include <bios.h>

#include <conio.h>

#define COM1 0

#define DATA_READY 0x100

#define TRUE 1

#define FALSE 0

#define SETTINGS (0x80 | 0x02 | 0x00 | 0x00)

int main(void)

{

int in, out, status, DONE = FALSE;

bioscom(0, SETTINGS, COM1);

cprintf("...BIOSCOM [ESC] для виходу...\n");

while(!DONE) {

status = bioscom(3, 0, COM1);

if (status & DATA_READY)

if ((out = bioscom(2, 0, COM1) & 0x7F) != 0)

putch(out);

if (kbhit()) {

if ((in = getch()) == '\x1B')

DONE = TRUE;

bioscom(1, in, COM1);

}

}

return(0);

}

Таймер BIOS та керування пам'яттю за допомогою функцій biosmemory.

Генерація звуків у комп'ютері PC, наприклад, виконується за допомогою програмувального таймера 8253, що застосовується для керування коливаннями динаміка. Керування коливаннями динаміка визначається частотою, що, у свою чергу, визначається вмістом різних внутрішніх регістрів. Значення цих регістрів встановлюються при записі у визначені порти. Порт 66 використовується для специфікації лічильника, що використовує таймер при визначенні інтервалу коливань динаміка. Таймер працює в строгій відповідності з частотою системного таймера і специфікованим значенням лічильника, що визначає коливання динаміка. Потім, після обнуління лічильника відбувається встановлення нового значення лічильника, і весь цикл функціонування програмувального таймера повторюється спочатку. Значення лічильника визначається по наступній формулі:

count = 1,193,180/необхідна частота

де 1,193,180 є тактова частота системного таймера.

Регістр-лічильник таймера 8253 встановлюється в наступній послідовності (значення лічильника задається двобайтним числом):

  1. Видати в порт 67 значення 182 (означаюче, що буде встановлюватися лічильник).

  2. Видати в порт 66 молодший байт числа, що визначає значення лічильника.

  3. Видати в порт 66 старший байт числа, що визначає значення лічильника.

Динаміки більшості комп'ютерів класу PC не дозволяють відтворювати повний спектр частот, сприйманих людським слухом (від 20 Гц до 18.000 Гц). Однак динамік дозволяє відтворювати ноти краще, ніж динаміки інших комп'ютерів у межах 12000 Гц і навіть вище. В основному ж динамік використовується в межах 100-5000 Гц.

Отже, таймер встановлений. Однак динамік ще не буде відтворювати звук, тому що не включений. Таймер 8253 активний постійно, а динамік вимагає додаткової команди включення. Активізація динаміка здійснюється шляхом встановлення значень бітів 0 і 1 регістра програмувального периферійного інтерфейсу, завдання значень якого виконується через порт 97. Якщо значення цих двох бітів встановлені (рівні 1), то динамік видає звук частотою, встановленої лічильником 8253. Якщо значення цих бітів рівні 0, то ніякий звук генеруватися не буде. Інші біти цього байта використовуються іншими пристроями, тому інтерпретація значення лівих бітів не може бути змінена. Таким чином, для встановлення значень керуючих динаміком біт необхідно виконати наступну послідовність дій:

  1. Одержати поточне значення регістра з порту 97.

  2. Порівняти це значення з 3 чи встановити рівним 3.

  3. Записати результат у порт 97.

Для того, щоб виключити динамік, необхідно переслати в порт значення 253.

Найпростішим прийомом, що дозволяє читати і писати байт із чи в порт, у С є використання відповідних функцій. У Турбо С - це функції inportb() і outportb(). У Microsoft C - це функції inp() і outp(). Вони мають наступний загальний формат:

int inportb(int port);

void outportb(int port, char value);

int inp(unsigned port);

int outp(unsigned port, int value);

В інших компіляторах С ці функції можуть мати інші назви, але обов'язково будуть присутні у вашій бібліотеці, тому що є одними з базових функцій версій С для ПЭВМ. У програмах, приведених у цьому параграфі, використовуються функції Турбо C.

biostime Читає чи встановлює таймер BIOS

Короткий приклад:

#include <bios.h>

long biostime(int cmd, long newtime);

Ця функція або зчитує, або встановлює таймер BIOS. Цей таймер відраховує час, що пройшов з напівночі, зі швидкістю, приблизно, 18.2 тіка в секунду. Функція biostime використовує переривання BIOS 0x1A.

Якщо аргумент cmd = 0, то функція повертає поточне значення таймера.

Якщо cmd = 1, то таймер встановлюється у відповідності зі значенням аргументу newtime типу long.

Коли функція biostime працює в режимі читання значення таймера BIOS (cmd = 0), вона повертає його поточне значення.

Приклад

#include <bios.h>

#include <time.h>

#include <conio.h>

int main(void)

{

long bios_time;

clrscr();

cprintf("Число тиків з напівночі: \r\n");

cprintf("Число секунд із напівночі: \r\n");

cprintf("Число хвилин з напівночі: \r\n");

cprintf("Число годин з напівночі: \r\n");

cprintf("\r\nнажмите будь-яку клавішу для виходу:");

while(!kbhit())

{

bios_time = biostime(0, 0L);

gotoxy(50, 1);

cprintf("%lu",bios_time);

gotoxy(50, 2);

cprintf("%.4f", bios_time / CLK_TCK);

gotoxy(50, 3);

cprintf("%.4f", bios_time / CLK_TCK / 60);

gotoxy(50, 4);

cprintf("%.4f", bios_time / CLK_TCK / 3600);

}

return 0;

}

_bios_timeofday чи Читає встановлює таймер BIOS

Короткий приклад:

#include <bios.h>

unsigned _bios_timeofday(int cmd,long *timep);

Ця функція або зчитує, або встановлює таймер BIOS. Цей таймер вважає час, що пройшов з напівночі, зі швидкістю, приблизно, 18.2 тика в секунду. Функція _bios_timeofday використовує переривання BIOS 0x1A.

Параметр cmd може мати одне з наступних значень:

_TIME_GETCLOCK Функція зберігає поточне значення таймера BIOS у комірці пам'яті, зазначеної timep. Якщо до таймера з напівночі не було звертань, функція повертає 1. У противному випадку - 0.

_TIME_SETCLOCK Функція встановлює таймер у значення типу long з покажчиком timep. Функція не повертає значення.

Функція _bios_timeofday повертає значення в значення регістр AX, встановлений при виклику таймера BIOS.

Приклад

#include <bios.h>

#include <time.h>

#include <conio.h>

int main(void)

{

long bios_time;

clrscr();

cprintf("Число тиків з напівночі: \r\n");

cprintf("Число секунд із напівночі: \r\n");

cprintf("Число хвилин з напівночі: \r\n");

cprintf("Число годин з напівночі: \r\n");

cprintf("\r\nнажмите будь-яку клавішу для виходу:");

while(!kbhit())

{

bios_timeofday(_TIME_GETCLOCK, &bios_time);

gotoxy(50, 1);

cprintf("%lu", bios_time);

gotoxy(50, 2);

cprintf("%.4f", bios_time / CLK_TCK);

gotoxy(50, 3);

cprintf("%.4f", bios_time / CLK_TCK / 60);

gotoxy(50, 4);

cprintf("%.4f", bios_time / CLK_TCK / 3600);

}

return 0;

}

biosmemory Повертає розмір пам'яті

Коротка довідка:

#include <bios.h>

int biosmemory(void);

Ця функція повертає розмір оперативної пам'яті, використовуючи переривання BIOS 0х12. Це значення не включає пам'ять адаптера дисплея, додаткову пам'ять і розширену пам'ять.

Розмір оперативної пам'яті в блоках по 1К. значення

Приклад

#include <stdio.h>

#include <bios.h>

int main(void)

{

int memory_syze;

memory_syze = biosmemory(); /*повертає значення до 640 К */

printf(" Розмір ОП = %dк\n", memory_size);

return 0;

}

Контрольні запитання:

  1. Робота з пам’яттю в С++ та Асемблер

  2. Файловий доступ

  3. Опис biosmemory

Лекція 25 «Резидентні програми операційної системи»