Лекция Соболевой

Вид материалаЛекция

Содержание


Чтение файла при помощи fgetc. Функция fgetc применяется для чтения символа из потока.
Чтение файла при помощи fgets
Чтение файла при помощи
Запись в файл при помощи fwrite
Запись в поток при помощи
Запись в поток при помощи
Форматный ввод из файла
Форматный вывод в файл
Команды обмена данными
Код команды
XLAT адресXLATB
1.13 Микропроцессор Intel х86. Арифметические команды. Логические команды и команды сдвига. Команды передачи управления.
Подобный материал:
1   2   3   4   5   6

Чтение файла при помощи fgetc. Функция fgetc применяется для чтения символа из потока.


int fgetc(FILE *fp);

В случае успеха, fgetc возвращает следующий байт или символ из потока (зависит от того, файл "двоичный" или "текстовый"). В противном случае, fgetc возвращает ссылка скрыта. (Отдельный тип ошибок можно определить вызовом ferror или feof с указателем на файл.)

Чтение файла при помощи fgets


Функция fgets применяется для чтения строки из потока. Считывание происходит до тех пор пока не будет достигнут конец строки (ссылка скрыта:0D0A, эквивалентны в ссылка скрыта \n) или длина строки n, в которую происходит считывание.

char *fgets(char *string, int n, FILE *fp);

Функция fgets возвращает указатель на строку string. В случае ошибки или конца файла возвращается значение NULL. Для определения того, что произошло - ошибка или конец файла, используются функции ссылка скрыта и ferror.

Чтение файла при помощи fread

int fread (char *buffer, int size, int count, FILE *fp);

Данная функция читает объекты размера size по счетчику count из входного потока fp и располагает их в буфере buffer. fread возвращает число действительно прочитанных объектов, которое меньше, чем count, если при чтении возникла ошибка или встретился конец файла.

Запись в файл при помощи fwrite


fwrite определяется как

int fwrite ( const char * array, size_t size, size_t count, FILE * stream );

Функция fwrite записывает блок данных в поток. Таким образом запишется ссылка скрыта элементов count в текущую позицию в потоке. Для каждого элемента запишется size байт. Индикатор позиции в потоке изменится на число байт, записанных успешно. Возвращаемое значение будет равно count в случае успешного завершения записи. В случае ошибки возвращаемое значение будет меньше count.

Запись в поток при помощи fputc

Функция fputc применяется для записи символа в поток.

int fputc(int c, FILE *fp);

Параметр c "тихо" конвертируется в unsigned char перед выводом. Если прошло успешно, то fputc возвращает записанный символ. Если ошибка, то fputc возвращает EOF.

Запись в поток при помощи fputs

int fputs (char *string, FILE *fp);

Функция fputs копирует строку в конец потока fp. Нулевой символ окончания '\0' не копируется. Возвращает последний записанный символ. Если вводимая строка string пустая, возвращается значение 0. Значение EOF свидетельствует об ошибке.

Форматный ввод из файла fscanf

int fscanf(FILE *fp, char *format[,arguments]);

Возвращает количество успешно проведенных преобразований. Процесс заканчивается при встрече первой неподходящей литеры или признака конца файла (EOF). Ноль на выходе означает, что не обработано ни одного поля. (format описан ниже =>)

fscanf("%i %i",&x1,&x2);

Форматный вывод в файл fprintf

int fprintf(FILE *fp, char *format[,arguments]);

Функция fprintf форматирует и печатает в выходной поток наборы символов и значений. Каждый аргумент (если он есть) преобразуется и выводится согласно заданной спецификации формата в строке формата. Строка формата (format) обладает той же формой и функцией, что и аргумент строки формата функции printf.

fprintf("Вывод = %d,%f\n",i,r);

=> Элемент формата(format) представляет собой последовательность :

% [ - ] [ длина поля ] [ . точность ] [ l ] спецификация типа

“ - “ означает сдвиг выводимых данных к левому краю поля ( по умолчанию данные сдвигаются вправо); длина поля - количество позиций для вывода; точность - количество дробных цифр для чисел или количество выводимых символов строки для строк; “ . ” - разделитель; l - означает, что число должно рассматриваться как long или double. Могут использоваться следующие спецификации типов :

d,i - для данных типа int;

u - для данных типа unsigned;

o - для данных типа unsigned (выводятся в восьмеричном виде);

x,X - для данных типа unsigned (выводятся в шестнадцатеричном виде), шестнадцатеричные цифры A,B,C,D,E,F изображаются большими буквами при использовании типа X и малыми буквами при использовании x;

c - для данных типа char, unsigned char (выводятся в символьном виде);

f - для данных типа float,double (выводятся в виде с фиксированной точкой, по умолчанию выводится 6 дробных цифр);

e,E - для данных типа float,double (выводятся в виде с плавающей точкой, по умолчанию выводится 7 цифр мантиссы и две цифры порядка, всего 13 позиций), отличаются изображением буквы E;

g,G - для данных типа float,double - величины 0.0001<|x|<1000000 выводятся в виде с фиксированной точкой с шестью значащими цифрами, другие величины выводятся с плавающей точкой с шестью значащими

цифрами (всего занимают 12 позиций), спецификации g и G различаются только изображением буквы e(E);

s - для строк;

p - для данных типа указатель.

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


Пример:

#include

int main (){

FILE *file;

char *fname = "file.txt";

file = fopen(fname,"r");

if(file == 0){ //проверка на успешное открытие файла

printf("не могу открыть файл '%s'",fname);

return 0;

}

char ch;

while((ch = fgetc(file))!=EOF) //читать пока не конец файла

fputc(ch, stdout); //вывод на экран(stdout-поток стандартного вывода, в нашем случае окно консоли)

fclose(file);

return 0;

}

1.12. Микропроцессор Intel х86. Регистры. Команды обмена данными. Команды работы со стеком.


Регистры.


Регистры общего назначения


32-битные регистры EAX (аккумулятор), EBX (база), ECX (счетчик), EDX (регистр данных) могут использоваться без ограничений для любых целей — временного хранения данных, аргументов или результатов различных операций. Названия этих регистров происходят от того, что некоторые команды применяют их специальным образом: так, аккумулятор часто используется для хранения результата действий, выполняемых над двумя операндами, регистр данных в этих случаях получает старшую часть результата, если он не умещается в аккумулятор, регистр-счетчик используется как счетчик в циклах и строковых операциях, а регистр-база используется при так называемой адресации по базе. Младшие 16 бит каждого из этих регистров могут использоваться как самостоятельные регистры и имеют имена (соответственно AX, BX, CX, DX). Кроме этого, отдельные байты в 16-битных регистрах AX – DX тоже имеют свои имена и могут использоваться как 8-битные регистры. Старшие байты этих регистров называются AH, BH, CH, DH, а младшие — AL, BL, CL, DL (рис. 3).





Другие четыре регистра общего назначения — ESI (индекс источника), EDI (индекс приемника), EBP (указатель базы), ESP (указатель стека) — имеют более конкретное назначение и могут применяться для хранения всевозможных временных переменных, только когда они не используются по назначению. Регистры ESI и EDI используются в строковых операциях, EBP и ESP используются при работе со стеком. Так же, как и с регистрами EAX – EDX, младшие половины этих четырех регистров называются SI, DI, BP и SP соответственно.


Регистры специального назначения


При использовании каждой из сегментированных моделей памяти для формирования любого адреса применяются два числа — адрес начала сегмента и смещение искомого байта относительно этого начала (в бессегментной модели памяти flat адреса начал всех сегментов равны). Операционные системы (кроме DOS) могут размещать сегменты, с которыми работает программа пользователя, в разных местах в памяти, и даже могут временно записывать их на диск, если памяти не хватает. Так как сегменты могут оказаться где угодно, программа обращается к ним, используя вместо настоящего адреса начала сегмента 16-битное число, называемое селектором. В процессорах Intel предусмотрено шесть шестнадцатибитных регистров — CS, DS, ES, FS, GS, SS, используемых для хранения селекторов. Это не значит, что программа не может одновременно работать с большим количеством сегментов памяти, — в любой момент времени можно изменить значения, записанные в этих регистрах.

В отличие от регистров DS, ES, GS, FS, которые называются регистрами сегментов данных, регистры CS и SS отвечают за сегменты двух особенных типов — сегмент кода и сегмент стека. Сегмент кода содержит программу, исполняющуюся в данный момент, так что запись нового селектора в этот регистр приводит к тому, что далее будет исполнена не следующая по тексту программы команда, а команда из кода, находящегося в другом сегменте, с тем же смещением. Смещение следующей выполняемой команды всегда хранится в специальном регистре — EIP (указатель инструкции, шестнадцатибитная форма IP), запись в который также приведет к тому, что следующей будет исполнена какая-нибудь другая команда. На самом деле все команды передачи управления — перехода, условного перехода, цикла, вызова подпрограммы и т.п. — и осуществляют эту самую запись в CS и EIP.


Команды обмена данными


· Команда:

MOV приемник, источник

· Назначение:

Пересылка данных

· Процессор:

8086

Базовая команда пересылки данных. Копирует содержимое источника в приемник, источник не изменяется. Команда MOV действует аналогично операторам присваивания из языков высокого уровня, то есть команда

mov ax,bx

эквивалентна выражению

ах := bх;

языка Паскаль или

ах = bх;

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

В качестве источника для MOV могут использоваться: число (непосредственный операнд), регистр общего назначения, сегментный регистр или переменная (то есть операнд, находящийся в памяти). В качестве приемника — регистр общего назначения, сегментный регистр (кроме CS) или переменная. Оба операнда должны быть одного и того же размера — байт, слово или двойное слово.

Нельзя выполнять пересылку данных с помощью MOV из одной переменной в другую, из одного сегментного регистра в другой и нельзя помещать в сегментный регистр непосредственный операнд — эти операции выполняют двумя командами MOV (из сегментного регистра в обычный и уже из него в другой сегментный) или парой команд PUSH/POP.


· Команда:

CMOVcc приемник, источник

· Назначение:

Условная пересылка данных

· Процессор:

P6

Это набор команд, которые копируют содержимое источника в приемник, если удовлетворяется то или иное условие (см. табл. 5). Источником может быть регистр общего назначения или переменная, а приемником — только регистр. Условие, которое должно удовлетворяться, — просто равенство нулю или единице тех или иных флагов из регистра FLAGS, но, если использовать команды CMOVcc сразу после команды СМР (сравнение) с теми же операндами, условия приобретают особый смысл, например:

cmp ах,bх ; сравнить ах и bх

cmovl ax,bx ; если ах < bх, скопировать bх в ах

Слова «выше» и «ниже» в таблице 5 относятся к сравнению чисел без знака, слова «больше» и «меньше» учитывают знак.


Таблица 5. Разновидности команды CMOVcc


Код команды

Реальное условие

Условие для CMP

CMOVA
CMOVNBE

CF = 0 и ZF = 0

если выше
если не ниже или равно

CMOVAE
CMOVNB
CMOVNC

CF = 0

если выше или равно
если не ниже
если нет переноса

CMOVB
CMOVNAE
CMOVC

CF = 1

если ниже
если не выше или равно
если перенос

CMOVBE
CMOVNA

CF = 1 и ZF = 1

если ниже или равно
если не выше

CMOVE
CMOVZ

ZF = 1

если равно
если ноль

CMOVG
CMOVNLE

ZF = 0 и SF = OF

если больше
если не меньше или равно

CMOVGE
CMOVNL

SF = OF

если больше или равно
если не меньше

CMOVL
CMOVNGE

SF <> OF

если меньше
если не больше или равно

CMOVLE
CMOVNG

ZF = 1 и SF <> OF

если меньше или равно
если не больше

CMOVNE
CMOVNZ

ZF = 0

если не равно
если не ноль

CMOVNO

OF = 0

если нет переполнения

CMOVO

OF = 1

если есть переполнение

CMOVNP
CMOVPO

PF = 0

если нет четности
если нечетное

CMOVP
CMOVPE

PF = 1

если есть четность
если четное

CMOVNS

SF = 0

если нет знака

CMOVS

SF = 1

если есть знак




· Команда:

XCHG операнд1, операнд2

· Назначение:

Обмен операндов между собой

· Процессор:

8086

Содержимое операнда 2 копируется в операнд 1, а старое содержимое операнда 1 — в операнд 2. XCHG можно выполнять над двумя регистрами или над регистром и переменной.

xchg eax,ebx ; то же, что три команды на языке С:

; temp = eax; eax = ebx; ebx = temp;

xchg al,al ; а эта команда не делает ничего

· Команда:

BSWAP регистр32

· Назначение:

Обмен байт внутри регистра

· Процессор:

80486

Обращает порядок байт в 32-битном регистре. Биты 0 – 7 (младший байт младшего слова) меняются местами с битами 24 – 31 (старший байт старшего слова), а биты 8 – 15 (старший байт младшего слова) меняются местами с битами 16 – 23 (младший байт старшего слова).

mov eax,12345678h

bswap eax ; теперь в еах находится 78563412h

Чтобы обратить порядок байт в 16-битном регистре, следует использовать команду XCHG:

xchg al,ah ; обратить порядок байт в АХ


· Команда:

IN приемник, источник

· Назначение:

Считать данные из порта

· Процессор:

8086

Копирует число из порта ввода-вывода, номер которого указан в источнике, в приемник. Приемником может быть только AL, АХ или ЕАХ. Источник — или непосредственный операнд, или DX, причем можно указывать только номера портов не больше 255.

· Команда:

OUT приемник, источник

· Назначение:

Записать данные в порт

· Процессор:

8086

Копирует число из источника (AL, АХ или ЕАХ) в порт ввода-вывода, номер которого указан в приемнике. Приемник может быть либо непосредственным номером порта, либо регистром DX. На командах IN и OUT строится все общение процессора с устройствами ввода-вывода — клавиатурой, жесткими дисками, различными контроллерами, и используются они, в первую очередь, в драйверах устройств. Например, чтобы включить динамик PC, достаточно выполнить команды:

in al,61h

or al,3

out 61h,al

· Команда:

CWD

· Назначение:

Конвертирование слова в двойное слово

· Процессор:

8086

· Команда:

CDQ

· Назначение:

Конвертирование двойного слова в учетверенное

· Процессор:

80386

Команда CWD превращает слово в AХ в двойное слово, младшая половина которого (биты 0 – 15) остается в АХ, а старшая (биты 16 – 31) располагается в DX. Команда CDQ выполняет аналогичное действие по отношению к двойному слову в ЕАХ, расширяя его до учетверенного слова в EDX:EAX. Эти команды всего лишь устанавливают все биты регистра DX или EDX в значение, равное значению старшего бита регистра АХ или ЕАХ, сохраняя таким образом его знак.

· Команда:

CBW

· Назначение:

Конвертирование байта в слово

· Процессор:

8086

· Команда:

CWDE

· Назначение:

Конвертирование слова в двойное слово

· Процессор:

80386

CBW расширяет байт, находящийся в регистре AL, до слова в АХ, CWDE расширяет слово в АХ до двойного слова в ЕАХ. CWDE и CWD отличаются тем, что CWDE располагает свой результат в ЕАХ, в то время как CWD, команда, выполняющая точно такое же действие, располагает результат в паре регистров DX:AX. Так же как и команды CWD/CDQ, расширение выполняется путем установки каждого бита старшей половины результата равным старшему биту исходного байта или слова, то есть:

mov al,0F5h ; AL = 0F5h = 245 = -11

cbw ; теперь АХ = 0FFF5h = 65 525 = -11


· Команда:

MOWSX приемник, источник

· Назначение:

Пересылка с расширением знака

· Процессор:

80386

Копирует содержимое источника (регистр или переменная размером в байт или слово) в приемник (16- или 32-битный регистр) и расширяет знак аналогично командам CBW/CWDE.

· Команда:

MOWZX приемник, источник

· Назначение:

Пересылка с расширением нулями

· Процессор:

80386

Копирует содержимое источника (регистр или переменная размером в байт или слово) в приемник (16- или 32-битный регистр) и расширяет нулями, то есть команда

movzx ax,bl

эквивалентна паре команд

mov al,bl

mov ah,0

· Команда:

XLAT адрес
XLATB

· Назначение:

Трансляция в соответствии с таблицей

· Процессор:

8086

Помещает в AL байт из таблицы в памяти по адресу ES:BX (или ES:EBX) со смещением относительно начала таблицы, равным AL. В качестве аргумента для XLAT в ассемблере можно указать имя таблицы, но эта информация никак не используется процессором и служит только как комментарий. Если этот комментарий не нужен, можно применить форму записи XLATB. В качестве примера использования XLAT можно написать следующий вариант преобразования шестнадцатеричного числа в ASCII-код соответствующего ему символа:

mov al,0Ch

mov bx, offset htable

xlatb

если в сегменте данных, на который указывает регистр ES, было записано

htable db "0123456789ABCDEF"

то теперь AL содержит не число 0Сh, а ASCII-код буквы «С».

· Команда:

LEA приемник, источник

· Назначение:

Вычисление эффективного адреса

· Процессор:

8086

Вычисляет эффективный адрес источника (переменная) и помещает его в приемник (регистр). С помощью LEA можно вычислить адрес переменной, которая описана сложным методом адресации, например по базе с индексированием. Если адрес 32-битный, а регистр-приемник 16-битный, старшая половина вычисленного адреса теряется, если наоборот, приемник 32-битный, а адресация 16-битная, то вычисленное смещение дополняется нулями.

Команду LEA часто используют для быстрых арифметических вычислений, например умножения:

lea bx,[ebx+ebx*4] ; ВХ=ЕВХ*5

или сложения:

lea ebx,[eax+12] ; ЕВХ=ЕАХ+12


· Команда:

PUSH источник

· Назначение:

Поместить данные в стек

· Процессор:

8086

Помещает содержимое источника в стек. Источником может быть регистр, сегментный регистр, непосредственный операнд или переменная. Фактически эта команда копирует содержимое источника в память по адресу SS:[ESP] и уменьшает ESP на размер источника в байтах (2 или 4). Команда PUSH практически всегда используется в паре с POP (считать данные из стека). Так, например, чтобы скопировать содержимое одного сегментного регистра в другой (что нельзя выполнить одной командой MOV), можно использовать такую последовательность команд:

push cs

pop ds ; теперь DS указывает на тот же сегмент, что и CS

Другое частое применение команд PUSH/POP — временное хранение переменных, например:

push eax ; сохраняет текущее значение ЕАХ

... ; здесь располагаются какие-нибудь команды,

; которые используют ЕАХ, например CMPXCHG

pop eax ; восстанавливает старое значение ЕАХ

· Команда:

POP приемник

· Назначение:

Считать данные из стека

· Процессор:

8086

Помещает в приемник слово или двойное слово, находящееся в вершине стека, увеличивая ESP на 2 или 4 соответственно. POP выполняет действие, полностью обратное PUSH. Приемником может быть регистр общего назначения, сегментный регистр, кроме CS (чтобы загрузить CS из стека, надо воспользоваться командой RET), или переменная. Если в роли приемника выступает операнд, использующий ESP для косвенной адресации, команда POP вычисляет адрес операнда уже после того, как она увеличивает ESP.

· Команда:

PUSHA
PUSHAD


· Назначение:

Поместить в стек все регистры общего назначения

· Процессор:

80186
80386

PUSHA помещает в стек регистры в следующем порядке: АХ, СХ, DX, ВХ, SP, ВР, SI и DI. PUSHAD помещает в стек ЕАХ, ЕСХ, EDX, ЕВХ, ESP, EBP, ESI и EDI. (В случае SP и ESP используется значение, которое находилось в этом регистре до начала работы команды.) В паре с командами POPA/POPAD, считывающими эти же регистры из стека в обратном порядке.


· Команда:

POPA
POPAD


· Назначение:

Загрузить из стека все регистры общего назначения

· Процессор:

80186
80386

Эти команды выполняют действия, полностью обратные действиям PUSHA и PUSHAD, за исключением того, что помещенное в стек значение SP или ESP игнорируется. РОРА загружает из стека DI, SI, BP, увеличивает SP на два, загружает ВХ, DX, CX, AX, a POPAD загружает EDI, ESI, ЕВР, увеличивает ESP на 4 и загружает ЕВХ, EDX, ЕСХ, ЕАХ.

1.13 Микропроцессор Intel х86. Арифметические команды. Логические команды и команды сдвига. Команды передачи управления.

Арифметические команды.