Примеры небольших подпрограмм на Ассемблере

 


Чтение строки с клавиатуры

Следующая процедура считывает строку 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

Назад

Сайт создан в системе uCoz