2.2. Доступ к оперативной памяти
В Си-программе программист имеет возможность прочитать и записать данные по абсолютному адресу памяти, а также определить физические адреса памяти, по которым располагаются переменные его программы. Для чтения данных из памяти испольэуются функции:
int peek(unsigned int segm, unsigned int offs);
char peekb(unsigned int segm, unsigned int offs);
Для записи данных в память испольэуются функции:
void poke(unsigned int segm, unsigned int offs, int val);
void pokeb(unsigned int segm, unsigned int offs, char val);
Аргументы этих функций: segm - сегментный адрес памяти, offs - смещение в сегменте. Функции peek и peekb возвращают прочитанное значение, а в функциях poke и pokeb записываемое в память значение задается аргументом val. Функции peekb и pokeb работают с одним байтом, peek и poke - с двухбайтным словом. Следует помнить, что слово в памяти ПЭВМ хранится в двух последовательных байтах памяти, причем в байте с меньшим адресом - младшая часть слова. Поэтому при побайтном (peekb) чтении из двух последовательных адресов мы получим сначала младшую, а затем старшую часть слова, а при чтении слова (peek) - целое слово, в котором обе части находятся "на своих местах".
Определение физических адресов переменных возможно благодаря наличию в языке Си данных типа "указатели", представляющих собой адреса переменных. Указатель может быть преобразован в физический адрес при помощи макросов:
unsigned int FP_SEG(void far *ptr);
unsigned int FP_OFF(void far *ptr);
которые возвращают сегментную часть адреса и смещение соответственно. Обратное преобразование производится макросом:
void far *MK_FP(unsigned int segm, unsigned int offs);
Следует учитывать, что внутреннее представление указателей зависит от модели памяти, в которой транслируется программа или от явного описания. Указатель может быть либо ближним (явное описание: near) и представляться двухбайтным смещением в текущем сегменте, либо дальним (явное описание: far) и иметь четырехбайтное представление - сегментная часть адреса и смещение. Указанные макросы работают только с дальними указателями.
Программные примеры данного пособия компилируются в модели памяти large (если в тексте программы не указана другая модель). По умолчанию в этой модели все указатели являются far. При компиляции в той модели памяти, где указатель по умолчанию near необходимо включить в описания всех указателей явные описатели far.
2.3. Порты ввода-вывода
Для чтения данных из порта испольэуются функции:
int inport(int port);
unsigned char inportb(int port);
Для записи данных в порт испольэуются функции:
void outport(int port, int val);
void outportb(int port, unsigned char val);
Аргумент этих функций port - номер порта ввода-вывода. Функции inport и inportb возвращают прочитанное из порта значение, а в функциях outport и outportb записываемое в порт значение задается аргументом val. Функции inportb и outportb работают с однобайтными, а inport и outport - с двухбайтными портами. Программист должен точно знать, работает он с одно- или с двухбайтным портом. Применение функций inport, outport к однобайтным портам может привести к весьма неожиданным результатам. Так, например, оператор программы:
outport(0x20,0x11);
(а 0x20 - однобайтный порт) эквивалентен двум операторам: outportb(0x20,0x11); outportb(0x21,0);
