Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
СП - ASM / Лабораторная работа №2 Видеопамять, методы адресации.doc
Скачиваний:
18
Добавлен:
03.03.2016
Размер:
293.89 Кб
Скачать

Сведения из теории

Определение типа ПЭВМ

В конце ПЗУ BIOS по адресу FF00:0FFE записан байт типа ПЭВМ. Специфицированы следующие 4 значения этого байта для машин фирмы IBM: 0xFF - IBM PC; 0xFE - XT; 0xFD - PCjr; 0xFC - AT. Для ПЭВМ других производителей стопроцентной гарантии дать нельзя.

адрес FF00:0FFE

Байт

Тип ПЭВМ

FF

PC

FE

PC XT

FD

PCjr

FC

PC AT

FA

PS/2 модель 30

F8

PS/2 модель 80

00

ЕС 1840

Фрагмент программы, определяющей тип ПЭВМ

unsigned char pc;

char *PT[]= { "AT", "PCjr", "XT", "IBM PC", "???" };

printf("Тип ПЭВМ = %Xh - ",pc=peekb(0xf000,0xfffe));

if ((pc-=0xfc)>4) pc=4;

printf("%s\n",PT[pc]);

Определение даты издания BIOS

8 байт ПЗУ начиная с адреса 0xFF00:0x0FF5, содержат дату выпуска данной версии BIOS в символьном виде в формате мм/дд/гг, например: 01/30/03.

Фрагмент программы определения даты BIOS:

unsigned int t;

printf("Дата издания BIOS = ");

for (t=0xfff5;t<0xfffd;t++)

printf("%c",peekb(0xf000,t));

printf("\n");

или

char *BIOSData;

printf("Дата издания BIOS: ");

BIOSData = (char *) MK_FP(0xFF00,0x0FF5);

for (i=0;i<=7;i++) {

printf("%c",BIOSData[i]);

}

printf("\n");

Определение размера ОЗУ (основной памяти)

Для хранения различных сведений о конфигурации ПЭВМ и режимах её работы используется область ОЗУ с адресами от 0040:0000h до 0050:0000h, называемая BIOS Data Area.

Объем ОЗУ (в Кбайтах) находится в области памяти BIOS по адресу 0040:0013 (2-байтное слово) и может быть получен или непосредственным чтением по этому адресу или при помощи прерывания 0x12, как это показано в следующем примере. Для определения объема ОЗУ в С существует специальная функция biosmemory(), объявленная в модуле bios.h.

printf("Объем ОЗУ %d Кб\n",peek(0x40,0x13));

union REGS rr;

int86(0x12,&rr,&rr);

printf("Объем ОЗУ: %d Кбайт\n",rr.x.ax);

printf("Объем ОЗУ: %d Кбайт\n",biosmemory());

Для ПЭВМ типа АТ (которые имеют энергонезависимую CMOS-память) объем ОЗУ может быть также прочитан из регистров 0x15 (младший байт) и 0x16 (старший байт) CMOS-памяти.

CMOS-память с точки зрения системного программиста представляет собой 64 однобайтовых ячеек памяти с мультиплексированной шиной адреса/данных и доступных для чтения/записи по адресам 70h (адрес ячейки) и 71h (данные ячейки). Для чтения их необходимо выдать в порт 0x70 байт номера регистра, а затем из порта 0x71 прочитать байт содержимого этого регистра.

printf(" Объем основной памяти (по данным CMOS) - ");

outportb(0x70,0x15);

for (i=1;i<=4000;i++);

f=inportb(0x71);

outportb(0x70,0x16);

for (i=1;i<=4000;i++);

f1=inportb(0x71);

printf("%d Кбайт\n",(f1<<8)|f);

printf("\n");

Кроме того, в AT может быть еще и расширенная (extended) память сверх 1 Мбайта (в AT она используется только для виртуальных дисков). Ее объем можно получить из регистров 0x17 (младший байт) и 0x18 (старший байт), или из регистров 0x30 (младший байт) и 0x31 (старший байт) или по функции 0x88 прерывания 0x15. К сожалению, содержимое этих регистров специфицировано для объемов дополнительной памяти до 15 Мб и на ПЭВМ, которые имеют больший объем ОЗУ выдают неверные результаты.

printf(" Объем extended памяти - ");

outportb(0x70,0x17);

for (i=1;i<=4000;i++);

f=inportb(0x71);

outportb(0x70,0x18);

for (i=1;i<=4000;i++);

f1=inportb(0x71);

printf("%u Кбайт\n",(f1<<8)|f);

Для выполнения этой же операции используется функции 0x88 прерывания 0 x15:

printf("Объем extended памяти: ");

rr.h.ah=0x88;

int86(0x15,&rr,&rr);

printf("%d Кбайт\n",rr.x.ax);

Определение состава оборудования

Определять состав оборудования следует только после того, как мы определили тип ПЭВМ. Это обусловлено тем, что способы получения информации о составе оборудования различны для различных типов ПЭВМ (например, XT и AT).

Наиболее важные элементы конфигурации ПЭВМ определяются процедурами POST при включении питания ПЭВМ. Результаты тестирования заносятся в область памяти BIOS. На основании этой информации BIOS формирует так называемый список оборудования - 2-байтное слово по адресу 0040:0010. Прочитать это слово можно либо обратившись по указанному адресу, либо обратившись к BIOS через прерывание 0x11. Назначения разрядов списка оборудования следующие:

Бит

Значение

0

- установлен в 1, если есть НГМД (см. разряды 6, 7);

1

- установлен в 1, если есть сопроцессор;

2,3

- число 16-Кбайтных блоков ОЗУ на системной плате, для PS/2 бит 2=1, если при POST обнаружен манипулятор «мышь»;

4,5

- код видеоадаптера: 11 - монохромный, 10 – цветной, 80 колонок, 01 - цветной, 40 колонок, 00 - другой;

6,7

- число НГМД (если бит 0 равен 1);

8

- 0, если установлен канал ПДП (для ХТ и АТ – всегда 0);

9,10,11

- число последовательных портов RS-232;

12

- 1, если установлен джойстик;

13

- 1, если установлен последовательный принтер только для PCjr (для ХТ и АТ – всегда 0);

14,15

- число параллельных принтеров.

Фрагмент программы, реализующей чтение слова состава оборудования:

// Чтение слова состава обоpудования из памяти BIOS

d=peek(0x40,0x10);

printf("Список оборудования");

for (i=15; i>=0; printf("%d",(d>>i--)&0x01));

printf(" (%04Xh)\n",d);

Далее необходимо расшифровать полученный код:

Определения наличия, количества и типа НГМД

Для получения дополнительной информации программа может обратиться к CMOS-памяти. С точки зрения определения конфигурации НГМД представляют интерес регистр с номером 10h CMOS-памяти. Младший полубайт этого регистра содержит информацию о НГМД А, старший – о НГМД В

Код

Значение

Младший полубайт (НГМД А)

0000

нет НГМД

0001

360 К

0010

1,2 Мб

0011

резерв

0100

1,44 Мб

0101 – 1111

зарезервировано

Старший полубайт (НГМД В)

0000

нет НГМД

0001

360 К

0010

1,2 Мб

0011

резерв

0100

1,44 Мб

0101 – 1111

зарезервировано

// "Раскодирование" списка обоpудования

printf(" Дисководов ГМД - ");

if (d&0x0001) {

printf("%d\n",FDDNum=((d&0x00c0)>>6)+1);

// определение типа дисковода */

outportb(0x70,0x10);

for (i=1;i<=4000;i++);

FDDType = inportb(0x71);

for (i=1;i<=FDDNum;i++) {

printf(" ГМД дисковод %d: тип ",i);

switch ((FDDType>>(i*4))&0x0F) {

case 0: printf("нет\n"); break;

case 1: printf("360 Кб"); break;

case 2: printf("1,2 Мб\n"); break;

case 3: printf("резерв"); break;

case 4: printf("1,44 Мб\n"); break;

default: printf("неизвестный тип (резерв)\n"); break;

}

}

}

Следует отметить, что CMOS-память содержит также данные о НЖМД (регистр 12h) - типы жестких дисков младший полубайт - для первого диска, старший - для второго; 0 в соответствующем полубайте означает, что этого жесткого диска нет; числа 1-14 - коды типов, число 15 означает, что тип жесткого диска записан в регистре 0x19 (для первого диска) или 0x1A (для второго). К сожалению, содержимое этих регистров специфицировано для НЖМД объемом до 512 Мб и не может быть использовано для современных ПЭВМ.

Определение наличия математического сопроцессора

Для этой операции необходимо проанализировать бит 1 слова состава оборудования. Дополнительно в ЯВУ С можно определить тип математического сопроцессора с использованием переменной _8087.

printf(" Сопроцессор x87 - ");

if (d&0x0002) {

printf("есть (");

// Определение типа сопроцессора

switch (_8087) {

case 1: printf("8087)\n"); break;

case 2: printf("80287)\n"); break;

case 3: printf("80387 или более современный)\n"); break;

}

}

else printf("нет\n");

Определения количества 16К модулей ОЗУ на системной плате (актуально для ХТ)

printf(" Количество 16К модулей ОЗУ на системной плате - ");

switch (d&0x000c) {

case 0: printf("0 \n"); break;

case 4: printf("1 \n"); break;

case 8: printf("2 \n"); break;

case 12: printf("3 или больше\n"); break;

}

Текущий режим адаптера дисплея

printf(" Текущий режим дисплейного адаптера - ");

switch (d&0x0030) {

case 0: printf("EGA/VGA"); break;

case 0x10: printf("40x25 - цветной"); break;

case 0x20: printf("80х25 - цветной"); break;

case 0x30: printf("MDA - монохромный"); break;

}

Количество установленных последовательных портов

Определяется аналогичным образом:

printf("\n Портов RS232 - %d\n",(d&0x0e00)>>9);

Базовые адреса установленных последовательных портов заносятся POST в область данных BIOS по адресам:

Адрес

Данные

0040:0000

Базовый адрес СОМ1

0040:0002

Базовый адрес СОМ2

0040:0004

Базовый адрес СОМ3

0040:0006

Базовый адрес СОМ4

Следует отметить, что хотя слово состояния может показывать до 7 установленных последовательных портов реально с использованием функций BIOS и MS-DOS можно работать с 4 СОМ портами.

Определение наличия манипулятора «джойстик»

Наличие манипулятора «джойстик» определяется аналогичным образом:

printf(" Джойстик - ");

if (d&0x1000) printf("есть\n");

else printf("нет\n");

При наличии манипулятора «джойстик» за ним закреплены адреса в диапазоне 200-20Fh (как правило, 201h)

Определение количества адаптеров параллельного порта

Аналогично:

printf(" Принтеров - %d\n",(d&0xe000)>>14);

Базовые адреса установленных адаптеров параллельного порта заносятся POST в область данных BIOS по адресам:

Адрес

Данные

0040:0008

Базовый адрес LPT1

0040:000A

Базовый адрес LPT2

0040:000C

Базовый адрес LPT3

0040:000E

Базовый адрес LPT4

Следует отметить, что POST определяет не более 3 адаптеров параллельного порта. Если с использованием функций BIOS и MS-DOS требуется использовать установленный LPT4, необходимо по адресу 0040:000E записать его базовый адрес.

Если необходимо получать информацию по всему списку оборудования, удобнее использовать такие структуры данных ЯВУ С как структуры с битовыми полями и объединения:

union EqListUnion {

unsigned EqWord;

struct EqListStruct {

unsigned FDD : 1; // наличие FDD

unsigned FPU : 1; // наличие FPU

unsigned MEM : 2; // и т.д.

unsigned Video : 2; //

unsigned FDDNum : 2; //

unsigned DMA : 1; //

unsigned RS232Num : 3; //

unsigned Joystick : 1; //

unsigned SerialPrn : 1; //

unsigned ParNum : 2; //

} EqList;

} Eq;

Тогда заполнить поля такой структуры можно одним оператором присваивания элемента EqWord:

// Чтение состава обоpудования через прерывание 0x11

int86(0x11,&rr,&rr);

Eq.EqWord = rr.x.ax;

А получать доступ к отдельным битам и их группам можно с использованием элемента EqList:

printf("НГМД (1-есть, 0-нет) %d\n",Eq.EqList.FDD);

printf("Сопроцессор (1-есть, 0-нет) %d\n",Eq.EqList.FPU);

printf("Количество 16К ОЗУ на плате %d\n",Eq.EqList.MEM);

Пример:

/*============= Получение списка оборудования ============*/

#include <dos.h>

#include <stdio.h>

#include <conio.h>

#include <bios.h>

main()

{

union REGS rr;

unsigned int d; /* список оборудования */

int i;

char NumOfBits = 16;

char mBits = 0;

int j;

union EqListUnion {

unsigned EqWord;

struct EqListStruct {

unsigned FDD : 1; // наличие FDD

unsigned FPU : 1; // наличие FPU

unsigned MEM : 2; //

unsigned Video : 2; //

unsigned FDDNum : 2; //

unsigned DMA : 1; //

unsigned RS232Num : 3; //

unsigned Joystick : 1; //

unsigned SerialPrn : 1; //

unsigned ParNum : 2; //

} EqList;

} Eq;

unsigned char pc; // Код типа PC

unsigned int t; // Текущее смещение для определения даты BIOS

int FDDNum;

unsigned char FDDType;

char *BIOSData;

clrscr();

char *PT[]= { "AT", "PCjr", "XT", "IBM PC", "???" };

printf("Тип ПЭВМ = %Xh - ",pc=peekb(0xf000,0xfffe));

if ((pc-=0xfc)>4) pc=4;

printf("%s\n",PT[pc]);

printf("Дата издания BIOS = ");

for (t=0xfff5;t<0xfffd;t++) printf("%c",peekb(0xf000,t));

printf("\n");

// Чтение байта обоpудования из памяти BIOS

// Будет получено то же самое

d=peek(0x40,0x10);

printf("Список активного оборудования из памяти BIOS - ");

for (i=15; i>=0; printf("%d",(d>>i--)&0x01));

printf(" (%04Xh)\n",d);

// "Раскодирование" списка обоpудования

printf(" Дисководов ГМД - ");

if (d&0x0001) {

printf("%d\n",FDDNum=((d&0x00c0)>>6)+1);

// определение типа дисковода */

outportb(0x70,0x10);

for (i=1;i<=4000;i++);

FDDType = inportb(0x71);

for (i=1;i<=FDDNum;i++) {

printf(" ГМД дисковод %d: тип ",i);

switch ((FDDType>>(i*4))&0x0F) {

case 0: printf("нет\n"); break;

case 1: printf("360 Кб"); break;

case 2: printf("1,2 Мб\n"); break;

case 3: printf("резерв"); break;

case 4: printf("1,44 Мб\n"); break;

default: printf("неизвестный тип (резерв)\n"); break;

}

}

}

else printf("нет\n");

printf(" Сопроцессор x87 - ");

if (d&0x0002) {

printf("есть (");

// Определение типа сопроцессора

switch (_8087) {

case 1: printf("8087)\n"); break;

case 2: printf("80287)\n"); break;

case 3: printf("80387 или более современный)\n"); break;

}

}

else printf("нет\n");

printf(" Количество 16К модулей ОЗУ на системной плате - ");

switch (d&0x000c) {

case 0: printf("0 \n"); break;

case 4: printf("1 \n"); break;

case 8: printf("2 \n"); break;

case 12: printf("3 илибольше\n"); break;

}

printf(" Объем ОЗУ (по данным BIOS) - %d Кбайт\n",peek(0x40,0x13));

unsigned char f, f1;

printf(" Объем основной памяти (по данным CMOS) - ");

outportb(0x70,0x15);

for (i=1;i<=4000;i++);

f=inportb(0x71);

outportb(0x70,0x16);

for (i=1;i<=4000;i++);

f1=inportb(0x71);

printf("%d Кбайт\n",(f1<<8)|f);

printf("\n");

printf(" Объем extended памяти - ");

outportb(0x70,0x17);

for (i=1;i<=4000;i++);

f=inportb(0x71);

outportb(0x70,0x18);

for (i=1;i<=4000;i++);

f1=inportb(0x71);

printf("%u Кбайт\n",(f1<<8)|f);

printf(" Текущий режим дисплейного адаптера - ");

switch (d&0x0030) {

case 0: printf("EGA/VGA"); break;

case 0x10: printf("40x25 - цветной"); break;

case 0x20: printf("80х25 - цветной"); break;

case 0x30: printf("MDA - монохромный"); break;

}

printf("\n Портов RS232 - %d\n",(d&0x0e00)>>9);

printf(" Джойстик - ");

if (d&0x1000) printf("есть\n");

else printf("нет\n");

printf(" Принтеров - %d\n",(d&0xe000)>>14);

printf("Для продолжения нажмите любую клавишу...");

getch();

clrscr();

printf("Дата издания BIOS: ");

BIOSData = (char *) MK_FP(0xFF00,0x0FF5);

for (i=0;i<=7;i++) {

printf("%c",BIOSData[i]);

}

printf("\n");

// Чтение состава обоpудования через прерывание 0x11

int86(0x11,&rr,&rr);

Eq.EqWord = rr.x.ax;

// Побитная распечатка слова оборудования

printf("Слово списка оборудования, полученного из прерывания 11h:\n");

for (i=NumOfBits-1; i>=0; i--) {

printf("%d",(rr.x.ax>>i)&0x01);

}

printf("b (%04Xh)",Eq.EqWord);

printf("\n|||||||||||||||+-- НГМД (1-есть, 0-нет) %d",Eq.EqList.FDD);

printf("\n||||||||||||||+--- Сопроцессор (1-есть, 0-нет) %d",Eq.EqList.FPU);

printf("\n||||||||||||\\/");

printf("\n|||||||||||| +---- Количество 16К ОЗУ на плате %d",Eq.EqList.MEM);

printf("\n||||||||||\\/");

printf("\n|||||||||| +------ Код видеоадаптера %d",Eq.EqList.Video);

printf("\n||||||||\\/");

printf("\n|||||||| +-------- Количество НГМД (00=1, 01=2, 10=3, 11=4) %d",Eq.EqList.FDDNum);

printf("\n|||||||+---------- Наличие оборудования КПДП (0 - есть) %d",Eq.EqList.DMA);

printf("\n||||\\ /");

printf("\n|||| +------------ Количество последовательных портов %d",Eq.EqList.RS232Num);

printf("\n|||+-------------- Наличие джойстика %d",Eq.EqList.Joystick);

printf("\n||+--------------- Наличие последовательного принтера (PCjr) %d",Eq.EqList.SerialPrn);

printf("\n\\/");

printf("\n +---------------- Количество адаптеров параллельного порта %d",Eq.EqList.ParNum);

int86(0x12,&rr,&rr);

printf("\nОбъем памяти по данным прерывания 12h: %d Кбайт\n",rr.x.ax);

printf("Объем памяти по данным biosmemory: %d Кбайт\n",biosmemory());

printf("Объем extended памяти: ");

rr.h.ah=0x88;

int86(0x15,&rr,&rr);

printf("%d Кбайт\n",rr.x.ax);

rr.h.ah=0x30;

intdos(&rr,&rr);

printf("Версия MS-DOS %d.%d\n",rr.h.al,rr.h.ah);

getch();

}

Содержание отчета

  1. Титульный лист.

  2. Текст программ(ы).

  3. Дампы областей памяти, содержащих информацию о составе оборудования.

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

Контрольные вопросы

  1. Для чего необходимо определять состав оборудования ПЭВМ?

  2. Какие параметры входят в слово состава оборудования ПЭВМ и почему?

  3. Чем отличается порт ввода-вывода от ячейки ОЗУ?

  4. В чем отличие логических и поразрядных логических операций?

  5. Что такое маска?

  6. Для чего могут быть использованы операции сдвига >> и <<?

  7. Для чего используются структуры?

  8. В чем отличие структур и объединений?

  9. Для чего используются объединения?

  10. Чем отличается структура от структуры с битовыми полями? В чем их преимущество?

  11. Какой размер могут иметь структуры с битовыми полями?

  12. Что определяет класс памяти переменной?

  13. В чем отличие использования псевдорегистров (AX_,BX_ и др.) и объединения REGS?

  14. Почему средствами языка С невозможно модифицировать содержимое регистра ВР?

  15. Почему в языке С реализовано несколько функций генерации программных прерываний?

  16. Можно ли использовать функции доступа к портам ввода-вывода для доступа к оперативной памяти? (объяснить почему).

  17. Какими способами можно осуществить доступ к ячейке оперативной памяти в языке С?

  18. Чем отличаются указатели near, far и huge?

  19. В чем состоят особенности логических и арифметических операций с указателями в языке С?

  20. Почему возникает необходимость использования динамических переменных?

  21. Где выделяется память под динамические переменные?

  22. Как программно определить наличие установленных микросхем ОЗУ по заданным адресам?

  23. Какой диапазон адресов в адресном пространстве ПЭВМ занимает КМОП память и каким образом осуществляется доступ к её ячейкам?

  24. В каких случаях имеет смысл использовать ассемблерные вставки в программу на языке С?

  25. В каких случаях имеет смысл применять раздельную компиляцию модулей на языке С и Ассемблера?

  26. Каким требованиям в случае использования раздельной компиляции должны удовлетворять программы на языке Ассемблера?

  27. Что такое макроопределение в языке С? В чем его отличие от функции?

  28. Что означает понятие «базовый адрес»?