Чтение строки с клавиатуры
Следующая процедура считывает строку ASCIIZ с клавиатуры.
KbdInput$ proc ;POW35
; Входные данные: смещение строки в AX
; Выходные данные: строка ASCIIZ, прочитанная с клавиатуры. Регистры не сохраняются.
mov DI,AX ;смещение строки
mov DX,AX ;смещение буфера
mov CX,255 ;максимальное количество читаемых символов
mov BX,0 ;файловый хэндл клавиатуры
mov AH,3Fh ;читаем из файла (фактически - с клавиатуры)
int 21h
jc Input$_error ;если ошибка
dec AX ;убираем символ RETURN
add DI,AX ;смещение байта, расположенного в конце строки
Input$_error:
mov [DI],BL ;завершаем строку, записывая 0 в конец строки
ret
KbdInput$ endp
Перевод чисел в двоичную форму (в виде строки)
Данная процедура конвертирует 16-битное слово в строку ASCIIZ, т.е. число 7 преобразовывается в строку 0000000000000111. Лидирующие нули включаются в строку. Строка ASCIIZ - это набор символов, завершающихся 0.
NmbrToBi$ proc ;POW36
;Входные данные: AX - смещение строки, BX - число, которое необходимо преобразовать
;Выходные данные: Строка ASCIIZ. Регистры не сохраняются.
mov DI,AX ;смещение строки
mov DX,8000h ;проверочное слово, 1 в позиции 15
mov CX,16 ;обрабатываем 16 бит
NumberTo_B0:
mov AL,48 ;символ '0'
test BX,DX ;бит равен 1?
jz NumberTo_B
inc AL ;символ '1'
NumberTo_B:
stosb ;записываем в строку '1' или '0'
shr DX,1 ;сдвигаем тестовый бит вправо
loop NumberTo_B0
mov [DI],DL ;завершаем строку 0
ret
NmbrToBi$ endp
Чтение значения счетчика времени
В памяти по адресу 40:6C расположено двойное слово, которое увеличивается на единицу приблизительно 18.2 раза в секунду. Системное время можно получить, считывая это слово. Младший байт может быть использован для многих "временных" задач, в т.ч. в качестве исходного значения для генератора псевдослучайных чисел (а в некторых случаях и заменить его).
GetTicks proc ;POW37
; Входные данные: нет
; Выходные данные: Младший байт счетчика времени в AX
; Регистры не сохраняются.
mov BX,ES ;Сохраняем адрес дополнительного сегмента
mov AX,40h ;сегмент данных BIOS
mov ES,AX
mov AX,ES:[6Ch] ;читаем счетчик
mov ES,BX ;восстанавливаем регистр ES
ret
GetTicks endp
Определяем тип процессора
Следующая процедура WhatCPU определяет тип процессора, установленного в системе. Результат возвращается в регистре AX. Процедура может быть откомпилирована и 16-битным компилятором, несмотря на то, что в ней используются 32-битные инструкции для определения различия между 386, 486 и Pentium.
WhatCPU proc ;POW38
;Результат в AX
;0: i88,i86, 1: i186, 2: i286, 3: i386, 4: i486, 5: Pentium
pushf ;сохраняем флаги
mov DX,0F000h
sub AX,AX
push AX ;записываем 0 в верхушку стека
popf ;восстанавливаем регистр флагов из стека
pushf ;записываем флаги в стек
pop AX
popf ;восстанавливаем флаги
and AX,DX ;выделяем четыре старших байта
cmp AX,DX ;они равны 1 ?
jne CPU_ei8088
mov AX,0 ;результат 0 (8088 или 8086)
ret
CPU_ei8088:
push SP
pop BX
cmp BX,SP ;изменяется ли указатель стека перед записыванием в него?
je CPU_ei186
mov AX,1 ;результат 1 (80186)
ret
CPU_ei186:
pushf ;сохраняем флаги
mov AX,DX ;0F000h
push AX
popf
pushf
pop AX
popf ;оригинальные флаги
and AX,DX
jne CPU_ei286
mov AX,2 ;результат 2 (80286)
ret
CPU_ei286:
db 66h
pushf ;pushfd
db 66h
pushf ;pushfd
db 66h
pop AX ;pop EAX
db 66h
or AX,0000h
db 04h,00h ;или EAX,00040000h
db 66h
push AX ;push EAX
db 66h
popf ;popfd
db 66h
pushf ;pushfd
db 66h
pop AX ;pop EAX
db 66h
popf ;popfd
db 66h
test AX,0000h
db 04h,00h ;test EAX,00040000h
jnz CPU_ei386
db 66h
mov AX,3 ;результат AX=00000003h (80386)
db 0h,0h
ret
CPU_ei386:
db 66h
pushf ;pushfd
db 66h
pushf ;pushfd
db 66h
pop AX ;pop EAX
db 66h
mov BX,AX ;mov EBX,EAX
db 66h
xor AX,0000h
db 20h,00h ;xor EAX,00200000h
db 66h
push AX ;push EAX
db 66h
popf ;popfd
db 66h
pushf ;pushfd
db 66h
pop AX ;pop EAX
db 66h
popf ;popfd
db 66h
and AX,0000h
db 20h,00h ;and EAX,00200000h
db 66h
and BX,0000h
db 20h,00h ;and EBX,00200000h
db 66h
cmp AX,BX ;cmp EAX,EBX
jne CPU_ei486
db 66h
mov AX,4 ;результат EAX=00000004h (80486)
db 0h,0h
db 66h ;обнуление 32 битных регистров
xor BX,BX ;xor EBX,EBX
ret
CPU_ei486: ;Pentium
db 66h
mov AX,5 ;результат EAX=00000005h (Pentium)
db 0h,0h
db 66h
xor BX,BX ;xor EBX,EBX
ret
WhatCPU endp
Установка видеорежимов VGA
Видеорежимы, поддерживаемые BIOS'ом адаптеров VGA BIOS:
Экран
Режим Текст Графика Цвета Размер Адрес
0 CGA 25*40 only text 16 B&W 2000 0B800h
1 CGA 25*40 only text 16 2000 0B800h
2 CGA 25*80 only text 16 B&W 4000 0B800h
3 CGA 25*80 only text 16 4000 0B800h
4 CGA 25*40 320*200 4 16000 0B800h
5 CGA 25*40 320*200 2 B&W 8000 0B800h
6 CGA 25*80 640*200 2 16000 0B800h
7 MDA 25*80 only text 2 4000
0Dh EGA 25*40 320*200 16 32000 0A000h
0Eh EGA 25*80 640*200 16 64000 0A000h
0Fh EGA 25*80 640*350 2 28000 0A000h
10h EGA 25*80 640*350 16 112000 0A000h
11h VGA 30*80 640*480 2 38400 0A000h
12h VGA 30*80 640*480 16 153600 0A000h
13h VGA 25*40 320*200 256 64000 0A000h
Требуемый видеорежим устанавливается вызовом функции BIOS
mov AH,0 ;POW39
mov AL,ScreenModeNumber
int 10h
Этот фрагмент также очищает экран. Содержимое AX не сохраняется. Стандартный BIOS не возвращает никакой информации, сигнализирующей об ошибке. В подерживаемых режимах можно читать и писать в видеопамять путем вызовов соответствующих функций (функции 8,9,0Ch,0Dh). Нормальный текстовый режим DOS - это режим 3.
Следующий фрагмент загружает набор символов из ROM в RAM и соответственно корректирует высоту отображения символов.
mov AH,11h ;изменить используемый набор символов и корректировать высоту их отображения
;mov AL,11h ;выбрать набор символов 8*14, 28 строк в режиме VGA
;mov AL,12h ;выбрать набор символов 8*8, 50 строк
mov AL,14h ;выбрать набор символов 8*16, 25 строк
mov BX,0 ;банк памяти генератора символов
int 10h
Линейные преобразования в системах с фиксированной точкой
Следующая подпрограмма переводит дюймы в миллиметры, но также она может быть использована для любых линейных преобразований, для чего достаточно изменить коэффициент преобразования.
Числа предствалены в 32-битном формате с фиксированной точкой. Старшее слово содержит целую часть числа, а младшее слово - дробную часть. Предполагается, что используются только положительные числа.
Код использует 32-битные инструкции, но может быть откомпилирован и 16-битным компилятором.
; данные
ConvFactor dw 26214 ;младший байт коэффициента преобразования 25.4
dw 25 ;старший байт
Inches dw 32768 ;младший байт представления 12.5 дюймов
dw 12 ;старший байт
mMeters dw ? ;младший байт результата в мм
dw ? ;старший байт
; код
db 66h
mov AX,Inches ;mov EAX,dword ptr ConvFactor
db 66h
mul ConvFactor ;результат в EDX:EAX
mov CL,16
db 66h
shr AX,CL ;shr EAX,16
mov mMeters,AX ;младший байт результата
mov mMeters+2,DX ;старший байт
Таблица размещения файлов FAT
Первый сектор (с номером 0) диска - это загрузочный сектор. Его первые байты содержат следующую информацию:
byte
0-2 Переход на загрузочную программу
3-10 Имя в ASCII формате или что-нибудь еще
11-12 Байт на сектор
13 Секторов на кластер
14-15 Секторов в загрузочной записи =B
16 Количество копий FAT
17-18 Количество каталогов в корне диска
19-20 Секторов на диск
21 Тип диска =xx
22-23 Секторов на FAT =F
и т.д.
Первая таблица FAT начинается с B. Ее копия располагается в секторе B+F etc.
Можно детально рассмотреть FAT используя утилиту DEBUG. Не вносите изменений в таблицу FAT на жестком диске, если вы не уверены, что вы делаете.
Первая запись таблицы FAT выглядит так:
12 bit FAT: xx 0FFh 0FFh
16 bit FAT: xx 0FFh 0FFh 0FFh
xx - тип диска.
Затем, с кластера 2 начинаются элементы таблицы. Возможные значения перечислены в следующей таблице:
12-бит.FAT 16-бит. FAT
000h 0000h пусто
002h-0FEFh 0002h-0FFEFh использовано кластеров.
Значение-указатель на следующую запись в цепочке.
0FF0h-0FF6h 0FFF0h-0FFF6h зарезервировано
0FF7h 0FFF7h bad
0FF8h-0FFFh 0FFF8-0FFFFh последний кластер в цепочке
Вы можете читать сектора, используя прерывание 25h. Отметим, что это прерывание сохраняет флаги в стеке, так что после выполнения прерывания они должны быть восстановлены
Запуск дочерней программы
DOS выделяет всю доступную память текущей программе, независимо от того, какой объем реально необходим. Поэтому вы должны освободить часть памяти для того, чтобы загрузить и выполнить дочернюю программу. Это выполняется процедурой Setmem. Каждый параграф занимает 16 байт. Пространство, необходимое текущей программе вычисляется как размер в параграфах = Lseg - Psp + 1
где Lseg - сегмент, расположенный после последнего байта программы, а Psp - сегмент, в котором расположен psp программы.
Setmem proc
;Выделяет AX параграфов памяти текущей программе
:и очищает всю остальную память.
;Входные данные: количество выделяемых параграфов в AX
;Выходные данные: число реально выделенных параграфов в AX
mov BX,AX ;объем выделяемой памяти в 16-битных параграфах
mov AH,4Ah
int 21h ;ES должен указыват на сегмент PSP программы
mov AX,BX ;число выделенных параграфов
ret
Setmem endp
Следующий фрагмент кода запускает программу CHILD.COM с параметром /HELP.
;сегмент данныхt:
ChildName db 'CHILD.COM',0 ;имя файла в виде строки ASCIIZ
; сегмент кода:
mov AX,CS
mov SegCmdLine,AX
mov SegFCB1,AX
mov SegFCB2,AX
push DS ;сохраняем регистры
push ES
mov CS:Shell_SS,SS ;сохраняем только регистр CS
mov CS:Shell_SP,SP
;exec-function
mov DX,offset ChildName ;DS:DX - указатель на строку, содержащую имя файла
mov AX,CS
mov ES,AX
mov BX,offset CS:Parm_Table ;таблица параметров ES:BX
mov AX,4B00h ;загрузить и выполнить программу
int 21h
cli ;запрещаем прерывания
mov SS,CS:Shell_SS ;восстанавливаем регистры
mov SP,CS:Shell_SP
sti ;разрешаем прерывания
pop ES
pop DS
cld ;флаг направления (direction flag) = 0
jc ThereWasError ;ошибка
; эти данные должны быть определены в сегменте кода
CmdLineTail db 6,' /HELP',13 ;6 - число символов
even ;faster this way
Shell_SS dw 0 ;указатель стека
Shell_SP dw 0
Parm_Table dw 0 ;наследуем переменные окружения родительской программы
dw offset CmdLineTail
SegCmdLine dw 0 ;сюда будет записан CS
dw 5Ch ;блок управления файлом (FCB) #1
SegFCB1 dw 0 ;сюда будет записан CS
dw 6Ch ;блок управления файлом (FCB) #2
SegFCB2 dw 0 ;сюда будет записан CS
Чтение параметров командной строки
Параметры командной строки (сразу после имени файла) могут быть прочитаны с помощью следующей процедуры ReadCL.
Например, если ваша программа называется KOE.COM и вы запускаете ее, набрав команду
KOE 4abcs
в командной строке DOS, то процедура ReadCL вернет строку 4abcs в формате ASCIIZ.
ReadCL proc ;чтение параметров командной строки в буфер по адресу ES:[DI]
;DS должен остаться неизменным после запуска программы (=PSP)
mov SI,80h ;адрес парамтеров
xor CX,CX
mov CL,[SI] ;длина в байтах
inc SI ;игнорируем байт длины
rep movsb ;перемещаем строку в буфер
mov AL,0
stosb ;завершаем строку ASCIIZ нулем
ret
ReadCL endp
TSR: Завершаемся и остаемся в памяти
Инсталляция TSR-программы выполняется в три этапа:
Загрузка резидентной части в память. Проверка, не находится ли наша программа уже в памяти. Сохранение необходимой информации для дальнейшего удаления резидента из памяти. Освобождение памяти, занятой копией переменных окружения для экономии.
Установка параметров для работы резидентной части. Обычно на этом этапе перехватываются прерывания.
Завершение установочной программы, при этом резидентная часть остается в памяти.
;Структура программы TSR
Begin: ;Здесь начинается .COM-программа
jmp Install
;Сюда нужно поместить резидентную часть
Install:
;сюда поместите код установки
mov AH,31h ;завершиться и остаться резидентом
mov AL,0 ;возвращает результат =OK
mov DX,offset Install
mov CL,4
shr DX,CL ;делим на 16
add DX,1 ;объем резидентной части программы
int 21h
Рисование в SVGA
Пикселы расположены линейно в памяти видеоадаптера. В 256-цветных режимах пиксел представляется одним байтом. Поэтому смещение точки с координатами (x,y) можно вычислить как 640*y+x в режиме с 640 пикселами по горизонтали. Единственное ограничение, связанное с такими вычисленими, - это то, что последний доступный пиксел, к которому может быть получен доступ, имеет координаты x=255, y=102, его смещение 65535. Это известное ограничение 64Kбайтных сегментов.
Чтобы обойти это ограничение, применяется переключение банков памяти. При этом переопределяется расположение физического адреса, которое соответствует логическому адресу. Так, логический адрес 0 соответствует физическому адресу 65536 если активен первый банк в видеодаптером с размером "окна" (granularity) 64 KB.
Логический адрес точки с координатами (x,y) определяется как 640*y+x-B*WG где B - номер банка и WG - размер "окна". Банк памяти может быть переключен с помощью функции AX=4F05h прерывания 10h в видеоадаптерах, поддерживающих стандарт VESA.
Следующая процедура рисует пиксел на экране. Координаты пиксела находятся в регистрах AX и BX, а в регистре CX передается цвет пиксела. В процедуре предполагается, что размер "окна" равен 64 KB, что справедливо, например, для чипов S3.
SVGA_bank dw 0 ;номер активного банка памяти
S_rivi dw 640 ;длина строки в байтах
VGA_seg dw 0A000h ;сегмент памяти экрана VGA
CBpxl$ proc ;рисует пиксел с координатами x=AX, y=BX, цвет=CX
xchg AX,BX ;теперь x=BX, y=AX
mul S_rivi
add AX,BX
adc DX,0 ;в DX помещается требуемый номер банка
mov DI,AX ;логический адрес
cmp DX,SVGA_bank ;банк корректен?
je Cxl256_OK
mov SVGA_bank,DX ;новый банк
mov AX,4F05h
xor BX,BX ;функция: устанавливаем банк DX, окно A
int 10h
Cxl256_OK:
mov BX,ES ;сохраняем сегмент
mov AL,CL ;цвет
mov ES,VGA_seg
stosb ;рисуем пиксел
mov ES,BX
ret
CBpxl$ endp
Пишем напрямую в видеопамять
; полностью завершенная COM-программа
codeseg segment
assume cs:codeseg, ds:codeseg, es:codeseg
org 100h
Code: jmp Start
x dw 50 ;координата x выводимого текста
y dw 20 ;координата y выводимого текста
Text db 'string to be printed',0 ;не забываем 0
Start:
mov AX,80 ;вычисляем адрес
mul y
add AX,x
shl AX,1 ;адрес в AX=160*y+2*x
mov DI,AX
mov SI,offset Text
push ES ;сохраняем ES
mov AX,0B800h ;сегмент экранной памяти в текстовом режиме
mov ES,AX
Print:
lodsb ;загружаем AL из DS:[SI]
or AL,AL ;конец строки?
jz Ready ;да, AL=0
mov ES:[DI],AL ;символ для отображения
add DI,2 ;пропускаем байт атрибутов
jmp Print
Ready: pop ES ;восстанавливаем ES
;---------------------------------------
int 20H
codeseg ends
end Code
Рисуем пиксел в графическом режиме
Графические режимы могут быть разбиты на шесть групп в зависимости от количества бит, отводимых каждому пикселу:
1 бит/пиксел, 2 цвета, одна битовая плоскость:
CGA mode 6 разрешение 640*200
2 бит/пиксел, 4 цвета, одна битовая плоскость:
CGA mode 4 разрешение 320*200
4 бит/пиксел, 16 цветов, четыре битовых плоскости:
EGA mode 0Dh разрешение 320*200
EGA mode 0Eh разрешение 640*200
EGA mode 10h разрешение 640*350
VGA mode 12h разрешение 640*480
VESA mode 102h разрешение 800*600
VESA mode 104h разрешение 1024*768
VESA mode 106h разрешение 1280*1024
8 бит/пиксел, 256 цветов, одна битовая плоскость:
VGA mode 13h разрешение 320*200
VESA mode 100h разрешение 640*400
VESA mode 101h разрешение 640*480
VESA mode 103h разрешение 800*600
VESA mode 105h разрешение 1024*768
16 бит/пиксел, 65536 цветов, одна битовая плоскость(существуют также 32768-цветные режимы):
VESA mode 111h разрешение 640*480
VESA mode 114h разрешение 800*600
24 бит/пиксел, 16777216 цветов, одна битовая плоскость:
VESA mode 112h разрешение 640*480
Исключая 4-битные режимы пикселы в памяти располагаются на одной плоскости (plane), т.е., если координаты пиксела (x,y), то адрес, по которому располагается этот пиксел в памяти может быть вычислен как
Address = LineLength*y + Bits*x/8
где LineLength - количество байтов, занимаемых каждой строкой пикселов, а Bits - количество бит, занимаемым пикселом.
Исключениями являются режимы CGA номер 4 и 6, у которых четные и нечетные линии расположены в различных сегментах памяти.
В шестнадцатицветных режимах экранная память разделяется на 4 битовые плоскости. Каждый бит значения цвета пиксела расположен на своей плоскости. Адрес байта, хранящего пиксел с координатами x,y можно вычислить как
Address = LineLength*y + x/8
где LineLength - число байтов, занимаемых одной строкой.
Рисование пиксела с координатами x,y в 16-цветных режимах подразумевает установку бита во всех четырех плоскостях. Активная в данный момент плоскость выбирается записью в соответствующие порты видеокарты.
Режимы CGA, EGA и VGA поддерживаются всеми стандартными BIOS. Переключение в эти режимы обычно осуществляется простым вызовом функций BIOS.
Pixel$
Во всех режимах VGA следующая процедура Pixel$ может нарисовать пиксел. Нужно отметить, что процедура достаточно медленная, т.к. используются вызовы функций BIOS.
Pixel$ proc
;Рисует пиксел во всех режимах VGA.
;Входные данные: x в AX, y в BX, цвет в CX
;Выходные данные: регистры не сохраняются
mov DX,BX ;строка y
xchg AX,CX ;CX - колонка x, AL - цвет
sub BH,BH ;0 страница
mov AH,0Ch ;выводим пиксел
int 10h
ret
Pixel$ endp
Самый интересный режим VGA - это режим 13h с возможностью отображения 256 цветов и разрешением 320*200. Номер цвета 0...255 соответствуют значениям в палитре, где все цвета представлены в виде определенных сочетаний красной, зеленой и синей компонент. Следующая процедура VGApxl$ рисует пиксел в этом режиме. Она работает достаточно быстро, однако существуют еще более быстрые варианты.
;данные:
VGA_seg dw 0A000h ;сегмент памяти экрана VGA
VGApxl$ proc
;Рисует пиксел в режиме VGA 13h.
;Входные данные: x в AX, y в BX, цвет в CX
;Выходные данные: регистры AX и BX не сохраняются
xchg BH,BL ;умножаем y на 256, BL=0
add AX,BX ;AX = x+256y
shr BX,1 ;делим 256y на два
shr BX,1 ;BX = 256y/4 = 64y
add BX,AX ;BX = x+320y
mov AX,ES ;сохраняем значение ES
mov ES,VGA_seg ;сегмент памяти экрана VGA
mov ES:[BX],CL ;выводим байт на экран
mov ES,AX ;восстанавливаем значение регистра ES
ret
VGApxl$ endp
Функция синуса в 32-битной системе с фиксированной точкой
Процедура Rsin$ вычисляет тригонометрическую функцию sin от 32-битного аргумента. 32-битная система с фиксированной точкой определяется следующим образом:
;значение переменной F32bit = 4.750
F32bit dw 49152 ;дробная часть (0.75*65536)
dw 4 ;целая часть
Использование процедуры:
Входные данные: смещение аргумента в BX, смещение результата в AX. Аргумент задает угол в градусах.
Выходные данные: значение функции sin, записываемое в переменную, смещение которой определяется регистром AX. Значения регистров не сохраняются.
Например, sin(30.5°) вычисляется так:
Angle dd 001E8000h ;старший байт=30, младший байт=32768
Result dd ? ;сюда будет записан результат
....
mov AX,offset Result
mov BX,offset Angle
call Rsin$
В результате такого вызова вы получите результат 0.50752 в то время как правильное значени еравно 0.50754
Rsin$ proc
; значение синуса аргумента (двойное слово по смещению BX) вычисленное как двойное слово по смещению AX.
; Угол (по смещению BX) в градусах в диапазоне -360...360.
push AX ;сохраняем смещение результата
mov AX,[BX+2] ;целая часть угла
mov CX,[BX] ;дробная часть угла
mov DX,1 ;знак результата - +1 или -1
or AX,AX ;какой знак?
jns Rsin_1
not AX ;меняем знак
not CX
add CX,1
adc AX,0
neg DX ;также меняется знак результата
Rsin_1: ;теперь имеем угол в диапазоне 0...360
; уменьшаем диапазон до 0...180
cmp AX,180 ;угол больше 180?
jl Rsin_2
sub AX,180
neg DX ;изменяем знак результата
Rsin_2: ;теперь угол AX:CX в диапазоне 0...179.99998
cmp AX,90 ;угол больше 90?
jl Rsin_3
mov BX,180 ;вычисляем 180-угол
sub SI,SI
sub SI,CX
sbb BX,AX
mov AX,BX
mov CX,SI
Rsin_3: ;угол в AX:CX в диапазоне 0...90
push DX ;сохраняем знак результата в стеке
cmp AX,90 ;угол равен 90?
jne R_sin4
mov AX,1 ;возвращаем 1.0000
sub BX,BX
jmp short Rsin_9
R_sin4:
mov SI,offset Rsin_t ;таблица значений синусов
add SI,AX
add SI,AX
sub AX,AX ;целая часть значения синуса
mov BX,[SI] ;дробная часть
or CX,CX ;дробная чать равна 0?
jz Rsin_9
; интерполяция между значениями [SI] и [SI+2]
mov AX,[SI+2]
sub AX,BX ;AX = sin(a+1)-sin(a)
mul CX ;CX=0... 0.9999, результат DX= AX*CX/65536
add BX,DX ;добавим получившийся результат к значению из таблицы
sub AX,AX ;целая часть результата
Rsin_9:
pop DX ;корректируем знак результата AX:BX
or DX,DX
jns Rsin_loppu
not AX ;изменяем знак результата
not BX
add BX,1
adc AX,0
Rsin_loppu:
pop DI ;смещение результата
mov [DI+2],AX ;записываем целую часть
mov [DI],BX ;дробная часть
ret
Rsin$ endp
;таблица синусов
Rsin_t dw 0,1144,2287, 3430, 4572, 5712, 6850, 7987, 9121,10252
dw 11380,12505,13626,14742,15855,16962,18064,19161,20252,21336
dw 22415,23486,24550,25607,26656,27697,28729,29753,30767,31772
dw 32768,33754,34729,35693,36647,37590,38521,39441,40348,41243
dw 42126,42995,43852,44695,45525,46341,47143,47930,48703,49461
dw 50203,50931,51643,52339,53020,53684,54332,54963,55578,56175
dw 56756,57319,57865,58393,58903,59396,59870,60326,60764,61183
dw 61584,61965,62328,62672,62997,63303,63589,63856,64104,64332
dw 64540,64729,64898,65048,65177,65287,65376,65446,65496,65526
dw 0 ;для интерполяции
Проверка готовности накопителя
Программа проверяет готовность устройства. Если устройство не готово, программа просит нажать клавишу ESC.
; Проверяем корректность и готовность устройства.
; Полностью завершенная COM-программа.
codeseg segment
assume CS:codeseg, DS:codeseg, ES:codeseg
org 100h
Begin: jmp Start
; ----переменные----
Intvec dd ? ;старый вектор прерывания 24h
Luukku db 'Disk not valid or ready. Hit Esc!',10,13,'$'
Start:
;------------ Основная программа -----------
;Перехватываем прерывание 24h
push ES
mov AX,3524h ;вектор int 24h записывается в ES:BX
int 21h
mov word ptr Intvec,BX ;смещение
mov word ptr Intvec[2],ES ;сегмент
pop ES
;load a new int 24h
mov AX,2524h ;новый вектор 24h
mov DX,offset CError ;адрес
int 21h
;код для проверки готовности устройства
mov DL,1 ;1 - A:, 2 - B: и т.д..
mov AH,36h ;функция определения свободного места на диске
int 21h
cmp AX,-1 ;AX - число секторов в кластере -1
je Loppu ;выход если нет диска или не готов
;устройство готово
;здесь ваш код....
Loppu: int 20h ;завершаем COM-программу
;-------- новое прерывание int 24h -----------------
assume DS:nothing ;будут использоваться дальние вызовы
CError proc far
pushf ;сохраняем флаги
or AH,AH
js EiLevyke
push DX
push DS
mov AX,CS
mov DS,AX
assume DS:Codeseg
mov DX,offset Luukku
mov AH,9 ;выводим строку DS:DX
int 21h
mov AH,0
int 16h ;ждем нажатия клавиши
cmp AL,27 ;это Esc ?
jne EiEsc
mov AH,4Ch ;завершаем программу
int 21h
EiEsc: pop DS
assume DS:nothing
pop DX
popf
mov AL,1 ;еще раз
iret ;возвращаем управление главной программе
EiLevyke: popf ;восстанавливаем флаги
jmp CS:Intvec ;вызываем старый обработчик int 24h
CError endp
codeseg ends
end Begin