Ушел с форума
Автор FAQ
15840 / 7422 / 994
Регистрация: 11.11.2010
Сообщений: 13,385
1

FAQ для раздела Assembler, MASM, TASM

29.05.2013, 11:38. Показов 134063. Ответов 65
Метки нет (Все метки)

Краткое содержание раздела F.A.Q.

Общее
Алгоритмы
Вычисления при помощи FPU
Вычисления при помощи СPU
Особенности программирования в DOS
Особенности программирования в Windows
23
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
29.05.2013, 11:38
Ответы с готовыми решениями:

Глоссарий для раздела Assembler, MASM, TASM
Thread, AFP, Charles Kludge, 6a6kin, Убежденный, Ethereal, выкладывайте свои глоссарии - разложу...

Организация тем в разделе Assembler, MASM, TASM
Всем привет! Друзья, возник вопрос: как лучше организовать темы в разделе Assembler, MASM, TASM?...

Полезные макросы для MASM и TASM
Не претендую на создание чего-то нового и гениального, но макросы довольно полезные. Часть из того,...

Видеоуроки по Ассемблеру MASM/TASM (для DOS) на русском языке
Всем доброго времени суток. Вобщем, ищу видеоуроки на русском языке по Ассемблеру. Нужно для...

MASM, TASM, FASM: что выбрать для программирования в ядре
Какой асемлер выбрать для проганья в едре? вынь

65
608 / 406 / 8
Регистрация: 26.04.2012
Сообщений: 2,065
29.05.2013, 19:16 2
Написать программу, выводящую строку "Hello, World!" на экран
  • masm exe вывод строки на экран 9 функцией DOS
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    .MODEL SMALL
    .STACK 100h
    .DATA
        HelloMessage DB 'Hello World',13,10,'$'
    .CODE
    START:
        mov ax,@data
        mov ds,ax
        mov ah,9
        mov dx,OFFSET HelloMessage
        int 21h
        mov ah,4ch
        int 21h
    END START
  • вывод через int 29h
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    .286
    .MODEL SMALL
    .STACK 100h
    .DATA
            HelloMessage DB 'Hello World',10,13
        num = $ - HelloMessage
    .CODE
    START:  push @data
            pop ds
        mov cx,num
    a1: lodsb
            int 29h
        loop a1
            mov ah,4ch
            int 21h
    end start
  • прямой вывод строки в видео память, выводит красные символы на черном фоне
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    .286
    .MODEL SMALL
    .STACK 100h
    .DATA
            HelloMessage DB 'Hello World'
        num = $ - HelloMessage
    .CODE
    START:  push @data
            pop ds
        push 0B800h
        pop es
        mov ax,3
        int 10h
        mov di,0
        mov si,offset HelloMessage
        mov cx,num
        mov ah,0Ch
    a1: lodsb
            stosw
        loop a1
            mov ah,4ch
            int 21h
    end start
  • masm com с директивами стандартной сегментации
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    CSEG SEGMENT
    assume cs:cseg,ds:cseg
    org 100h
    start:  jmp begin
    msg db 'Hello World!'
    begin:  mov ah,40h
        mov bx,1
        mov cx,12
        mov dx,offset msg
        int 21h
        ret
    CSEG ENDS
    end start
  • Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    CSEG SEGMENT
    assume cs:cseg,ds:cseg
    org 100h
    start:  mov ah,40h
        mov bx,1
        mov cx,12
        mov dx,offset msg
        int 21h
        ret
    msg db 'Hello World!'
    CSEG ENDS
    end start
  • вывод функцией BIOS
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    .model tiny
    .286
    .code
    org 100h
    start:
    mov ah,13h
    xor dx,dx
    mov cx,sizeof string
    mov bp,offset string
    mov bx,0ch
    int 10h
    ret
    string db 'Hello World!'
    end start
  • Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    .data
    HELLO DB 'Здравствуй мир!',0 ;строка для вывода 
    .code
    MOV AH,0Eh ; на экран номер подфункции BIOS
    mov si,offset HELLO ;SI указывает на строку
    next: lodsb ;помещаем символ в AL и переходим к следующему символу,
     INT 10h ;выводим символ на экран
    test al,al ;проверяем на конец строки
    jnz next ;если нет — повторяем все сначала
  • fasm консольная программа для Windows
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    
    ; hello - example of tiny (one section) Win32 program
     
    format PE console 4.0
     
    include 'include\win32ax.inc'
    ENABLE_PROCESSED_OUTPUT  = 00000001
     
        invoke  AllocConsole
        invoke  SetConsoleCP, 1251
        invoke  SetConsoleOutputCP, 1251
        invoke  GetStdHandle, STD_INPUT_HANDLE
            push    eax
            push    eax
        invoke  GetStdHandle, STD_OUTPUT_HANDLE
            push    eax
        invoke  SetConsoleMode, eax, ENABLE_PROCESSED_OUTPUT
        pop eax
        invoke  WriteConsole, eax, hello, msgsz, NULL, NULL
    ;   pop eax
        invoke  FlushConsoleInputBuffer
        pop eax
        invoke  ReadConsole, eax, buf, bufsz, cnt, NULL
    ;exit:
        invoke  FreeConsole
        invoke  ExitProcess,0
     
    cnt dd  ?
    buf db  10 dup(?)
    bufsz   =   $ - buf
    hello   db  'cp1251 Привет из консоли!',0
    msgsz   =   $ - hello
    ; import data in the same section
     
    data import
     
     library kernel32,'KERNEL32.DLL'
     
     import kernel32,\
        ExitProcess,'ExitProcess',\
        SetConsoleCP,'SetConsoleCP',\
        SetConsoleOutputCP,'SetConsoleOutputCP',\
        GetStdHandle,'GetStdHandle',\
        SetConsoleMode,'SetConsoleMode',\
        ReadConsole,'ReadFile',\
        WriteConsole,'WriteConsoleA',\
        FreeConsole,'FreeConsole',\
        FlushConsoleInputBuffer,'FlushConsoleInputBuffer',\
        AllocConsole,'AllocConsole'
    end data
  • разноцветная надпись в DOS
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    .286 
    .model tiny
    .code
    org 100h 
    start: mov bp,offset ABC
        mov ax,1303h
        mov bx,7
        mov cx,16
        xor DX,DX
        int 10h 
        retn
    ABC db 'H',0Ah,'e',0Bh,'l',0Dh,'l',0Ch
        db 'o',0Bh,',',0Ah,' ',0Ah,'W',09h
        db 'o',08h,'r',07h,'l',06h,'d',05h
        db '!',02h,'!',02h,'!',02h
    end start
  • Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    .286
    .model tiny
    .code
    org 100h
    start:  mov si,offset string
        mov cx,N
        mov ah,2
    @@: lodsb
        mov dl,al
        int 21h
        loop @b
        retn
    string db 'Hello, world!'
    N = $ - string
    end start
  • Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    .286
    .model tiny
    .code
    org 100h
    start:  mov si,offset string
        mov cx,N
        mov ah,6
    @@: lodsb
        mov dl,al
        int 21h
        loop @b
        retn
    string db 'Hello, world!'
    N = $ - string
    end start
  • Пример вызова функции DOS через альтернативный обработчик прерывания 21h.
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    .286
    .model tiny
    .code
    org 100h
    start: push offset RETURN     ;Занести в стек флаги, сегмент
            push cs                    ;и смещение адреса возврата
            pushf                      ;в обратном порядке.
            mov cl,9                 ;Функция: показать строку.
            mov dx,offset MESSAGE    ;Загрузить адрес сообщения.
            db 0EAh,0C0h,0,0,0     ;jmp far ptr 0:0C0h
    RETURN: mov ah,4Ch               ;Завершить процесс через DOS.
            int  21h                   
    MESSAGE db 'Hello, world!$'
    end start
  • имитируем вызов int 21h
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    .286
    .model tiny
    .code
    org 100h
    start:
    ;получаем дальний адрес (cs:ip) обработчика 
    ;прерывания 21h из таблицы векторов прерывания
        push 0;сегментный адрес таблицы векторов прерывания в es    
        pop es      
        mov di,es:[21h*4];смещение обработчика прерывания 21h в di
        mov si,es:[21h*4+2];сегмент обработчика прерывания 21h в si
        mov dx,offset string
        mov ah,9      ;номер функции
    ;три параметра в стек для возврата из прерывания
        pushf           
            push cs
            push offset @f  ;адрес возврата
        push si     ;cs для int 21h
        push di     ;ip для int 21h
        retf        ;подменяем cs и ip
    @@: mov ah,4Ch      ;выход из программы
        int 21h
    string db 'Hello, world!$'
    end start
  • DPMI client [FASM]
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    
    format  MZ
     
        LF  equ 10
        CR  equ 13
     
        entry   _TEXT:start
     
        segment _TEXT use16
     
    start:
        push    cs
        pop ds
        mov ax, ss
        mov cx, es
        sub ax, cx
        mov bx, sp
        shr bx, 4
        inc bx
        add bx, ax          ;release unused DOS memory
        mov ah, 4Ah
        int 21h
        mov ax, 1687h       ;DPMI host installed?
        int 2Fh
        and ax, ax
        jnz nohost
     
        push    es          ;save DPMI entry address
        push    di
        and si, si          ;requires host client-specific DOS memory?
        jz  nomemneeded
        mov bx, si
        mov ah, 48h         ;alloc DOS memory
        int 21h
        jc  nomem
        mov es, ax
    nomemneeded:
        mov bp, sp
        mov ax, 0001        ;start a 32-bit client
        call    far [bp]        ;initial switch to protected-mode
        jc  initfailed
    ;now in protected-mode
    ; CS = 16-bit selector corresponding to real-mode CS
    ; SS = selector corresponding to real-mode SS (64K limit)
    ; DS = selector corresponding to real-mode DS (64K limit)
    ; ES = selector to program's PSP (100h byte limit)
    ; FS = GS = 0
        mov cx,1            ;get a descriptor for the 32-bit code segment
        mov ax,0
        int 31h
        jc  dpmierr
        mov bx,ax                   ; base selector
        mov dx,_TEXT32
        mov cx,dx
        shl dx,4
        shr cx,12                   ; now CX:DX = linear(flat) _TEXT32 addr
        mov ax,7            ;set base
        int 31h
        or  dx,-1
        xor cx,cx
        mov ax,8            ;set limit
        int 31h
        mov cx,cs
        lar cx,cx
        shr cx,8
        or  ch,40h          ;make a 32bit CS
        mov ax,9
        int 31h
        push    bx          ; _TEXT32 selector
        push    start32
        retf                ;jump to 32bit CS
    nohost:
        mov dx, dErr1
    error:
        mov ah, 9
        int 21h
        mov ax, 4C00h
        int 21h
    nomem:
        mov dx, dErr2
        jmp error
    initfailed:
        mov dx, dErr3
        jmp error
    dpmierr:
        mov dx, dErr4
        jmp error
     
    ;   segment _DATA use16
     
    szWelcome   db      "Good bye, ugly 16 bit!",CR,LF
            db  "Hello, world from DOS protected mode 32-bit client!",CR,LF,'$'
    dErr1       db  "no DPMI host installed",CR,LF,'$'
    dErr2       db  "not enough DOS memory for client initialisation",CR,LF,'$'
    dErr3       db  "DPMI initialisation failed",CR,LF,'$'
    dErr4       db  "no LDT descriptors available",CR,LF,'$'
     
    ;--- the 32-bit code segment
     
        segment _TEXT32 use32
     
    start32:
        mov edx, szWelcome      ;print welcome message
        mov ah,9
        int 21h
        mov ah,0
        int 16h
        mov ax, 4C00h       ;return to DOS
        int 21h
  • Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
    .386
        .model flat, stdcall
        option casemap :none
        include \masm32\include\windows.inc
        include \masm32\include\kernel32.inc
        includelib \masm32\lib\kernel32.lib
    .data
    msg db "Hello World"
    stdout dd ?
    cWritten dd ?
    .code
    start:
    invoke GetStdHandle,STD_OUTPUT_HANDLE
    mov stdout,eax
    invoke WriteConsoleA,stdout,ADDR msg,SIZEOF msg,ADDR cWritten,NULL
    invoke ExitProcess,0
    end start
  • Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    .386
        .model flat, stdcall
        option casemap :none
        include \masm32\include\masm32.inc
        include \masm32\include\kernel32.inc
        include \masm32\macros\macros.asm
        includelib \masm32\lib\masm32.lib
        includelib \masm32\lib\kernel32.lib
        .code
        start:
          print "Hello world"
          exit
        end start
  • Графика [FASM]
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    
        format MZ
        heap    0
        stack   100h
        entry   main:start
    segment main    use16
    start:  push    0                   
        pop fs                      ; vector table
        mov ax,data_segment
        mov ds,ax
        mov es,ax
        mov ax, 13h            ; modeX Graphics 320x200x256, 40x25, 8x8     
        int 10h
        mov eax, dword [fs:43h*4]
        mov dword [int43], eax     ; Get 8x8 chargen ptr
        mov si, hello
    next:   call    gotoxy
        lodsb
        or  al,al
        jz  exit
        cmp al,20h  ; <space>?
        jnz @1
        add byte [Y], 11
        mov byte [X], 0
        jmp next
    @1: movzx   ax,al
        shl ax, 3   ; ax*8
        push    si
        push    ds
        lds si, [int43]
        add si, ax
    ; Вывод на экран 
        mov cx, 8 
    loo0:   lodsb
        mov bl, al
        push    cx
        mov cx, 8
    loo1:   mov al, 20h
        rcl bl, 1
        jnc @@1
        mov al, 0DBh
    @@1:    int 29h
        loop    loo1
        pop cx
        inc byte [es:Y]
        call    gotoxy
        loop    loo0
        pop ds
        pop si
        add byte [X], 8
        sub byte [Y], 8
        jmp next    
    ; выход
    exit:   xor ah, ah
        int 16h
        mov ax, 03h
        int 10h
        mov ah, 4Ch
        int 21h
    gotoxy: pusha
        mov ah,2
        xor bx,bx
        mov dx, word [es:XY]
        int 10h
        popa
        ret
    segment data_segment use16 
    int43:  dd  ?
    XY:
    X:  db  0
    Y:  db  3
    hello:  db  'Hello world',0
  • шлём сообщение самому себе через LAN Manager[FASM]
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
            org 100h
            push    cs
            pop ax
            mov [rcpt_seg], ax
            mov [msg_seg], ax
            mov ax, 5E00h   ; DOS 3.1+ network
            mov dx, wkst_nm ; получить
            int 21h             ; имя локальной машины
            mov ax, 5F40h       ; LAN Manager Enhanced DOS
            mov dx, LM_MSG  ; отправить сообщение
            int 21h             ; самому себе
            int 20h
    ; LAN Manager NetMessageBufferSend parameter structure:
    LM_MSG:                                 ;
    ; DWORD -> имя адресата (name for specific user, name* for domain wide name, * for broadcast)
    rcpt_ofs:   dw  wkst_nm
    rcpt_seg:   dw  ?
    ; DWORD -> текст сообщения
    msg_ofs:    dw  msg
    msg_seg:    dw  ?
    msg_sz:     dw  msg_len     ; длина сообщения
    ;
    wkst_nm:    db  10h dup(?)  ; имя рабочей станции
    msg     db  'Hello, World!'
    msg_len     =   $ - msg
  • Hello world через ESC-последовательности.
    предварительно должен быть загружен драйвер ANSY.SYS
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    .model small
    .stack 256
    .data
         
         msg db 27, '[2J'                              ;очистка экрана
         db 27, '[31;47m'              ;красные символы на белом фоне
         db 27, '[12;40H', 'Hello, world!!!'    ;текст выводится начиная с 12 строки 40 колонки
         db 27, '[0m', 27, '[25;1H$'    ;отменить аттрибуты вывода текста и установить курсор на 25 строку 1 стобец
     
    .code
    start:
        mov ax, @data
        mov ds, ax
        
        mov ah, 9
        mov dx, offset msg
        int 21h
        
        xor ax, ax
        int 16h
        mov ax, 4c00h
        int 21h
    end start
  • FASM 64 бита "Привет мир!"

    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    
    ; пример 64-битного PE файла
    ; для его выполнения необходимо иметь Windows XP 64-bit edition
     
    ; указываем формат
    format PE64 GUI
     
    ; указываем точку входа
    entry start
     
    ; создать кодовую секцию с атрибутами на чтение и исполнение
    section '.code' code readable executable
    start: 
      sub rsp,8     ; Make stack dqword aligned
            mov        r9d,0                   ; uType == MB_OK (кнопка по умолчанию)
                                               ; аргументы по соглашению x86-64
                                               ; передаются через регистры, не через стек!
                                               ; префикс d задает регистр размером в слово,
                                               ; можно использовать и mov r9,0, но тогда
                                               ; машинный код будет на байт длиннее
     
            lea        r8,[_caption]           ; lpCaption, передаем смещение
                                               ; команда lea занимает всего 7 байт,
                                               ; а mov reg,offset - целых 11, так что
                                               ; lea намного более предпочтительна
     
            lea        rdx,[_message]          ; lpText, передаем смещение выводимой строки
     
            mov        rcx,0                   ; hWnd, передам дескриптор окна-владельца
                                               ; (можно также использовать xor rcx,rcx
                                               ; что на три байта короче)
     
            call       [MessageBox]            ; вызываем функцию MessageBox
     
            mov        ecx,eax                 ; заносим в ecx результат возврата
                                               ; (Функция ExitProcess ожидает 32-битный параметр;
                                               ; можно использовать и mov rcx,rax, но это будет
                                               ; на байт длиннее)
     
            call       [ExitProcess]           ; вызываем функцию ExitProcess
     
    ; создать секцию данных с атрибутами на чтение и запись
    ; (вообще-то, в данном случае атрибут на запись необязателен,
    ; поскольку мы ничего не пишем, а только читаем)
    section '.data' data readable writeable
     
      _caption db 'Win64 program template',0     ; ASCIIZ-строка заголовка окна
      _message db 'Hello World!',0             ; ASCIIZ-строка выводимая на экран
     
    ; создать секцию импорта с атрибутами на чтение и запись
    ; (здесь атрибут на запись обязателен, поскольку при загрузке PE-Файла
    ; в секцию импорта будут записываться фактические адреса API-функций)
    section '.idata' import data readable writeable
     
            dd 0,0,0,RVA kernel_name,RVA kernel_table
            dd 0,0,0,RVA user_name,RVA user_table
            dd 0,0,0,0,0     ; завершаем список двумя 64-разряными нулеми!!!
     
    kernel_table:
            ExitProcess dq RVA _ExitProcess
            dq 0                        ; завершаем список 64-разряным нулем!!!
     
    user_table:
            MessageBox dq RVA _MessageBoxA
            dq 0
     
    kernel_name db 'KERNEL32.DLL',0
    user_name db 'USER32.DLL',0
     
    _ExitProcess dw 0
            db 'ExitProcess',0
    _MessageBoxA dw 0
            db 'MessageBoxA',0
Troll_Face,
извини, что воспользовался твоим топиком, но первое сообщение в топике будет таскаться за ним во всех страницах
5
Эксперт быдлокодинга
2091 / 525 / 69
Регистрация: 04.11.2010
Сообщений: 1,310
29.05.2013, 19:22 3
Вывод спирали на экран в DOS

Теория

Архимедова спираль — спираль, плоская кривая, траектория точки M, которая равномерно движется вдоль луча OV с началом в O, в то время как сам луч OV равномерно вращается вокруг O. Другими словами, расстояние https://www.cyberforum.ru/cgi-bin/latex.cgi?\rho = OM пропорционально углу поворота https://www.cyberforum.ru/cgi-bin/latex.cgi?\varphi луча OV. Повороту луча OV на один и тот же угол https://www.cyberforum.ru/cgi-bin/latex.cgi?\varphi соответствует одно и то же приращение https://www.cyberforum.ru/cgi-bin/latex.cgi?\rho.
Уравнение Архимедовой спирали в полярной системе координат записывается так:
https://www.cyberforum.ru/cgi-bin/latex.cgi?\rho = k \cdot \varphi
где k — смещение точки M по лучу r, при повороте на угол равный одному радиану.
Повороту прямой на https://www.cyberforum.ru/cgi-bin/latex.cgi?2\pi соответствует смещение a = |BM| = |MA| = https://www.cyberforum.ru/cgi-bin/latex.cgi?2k\pi. Число a — называется шагом спирали. Уравнение Архимедовой спирали можно переписать так: https://www.cyberforum.ru/cgi-bin/latex.cgi?\rho =\frac{a}{2\pi }\varphi
При вращении луча против часовой стрелки получается правая спираль, при вращении — по часовой стрелке — левая спираль.
Обе ветви спирали (правая и левая) описываются одним уравнением (1). Положительным значениям https://www.cyberforum.ru/cgi-bin/latex.cgi?\varphi соответствует правая спираль, отрицательным — левая спираль. Если точка M будет двигаться по прямой UV из отрицательных значений через центр вращения O и далее в положительные значения, вдоль прямой UV, то точка M опишет обе ветви спирали.
Луч OV, проведенный из начальной точки O, пересекает спираль бесконечное число раз — точки B, M, A и так далее. Расстояния между точками B и M, M и A равны шагу спирали https://www.cyberforum.ru/cgi-bin/latex.cgi?a =2k\pi. При раскручивании спирали, расстояние от точки O до точки M стремится к бесконечности, при этом шаг спирали остается постоянным (конечным), то есть, чем дальше от центра, тем ближе витки спирали, по форме, приближаются к окружности.

Практика

Две пересекающиеся спирали. X и Y рассчитываются по формулам:
  • X=alpa*шаг_спирали*Cos(alpha)
  • Y=alpa*шаг_спирали*Sin(alpha)
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
; masm dos exe #
.model small
.data
delta dd 0.001 ;величина изменения
divK dd 5.0 ;расстояние между дугами
xr dw 0 ;координаты выводимой точки
yr dw 0 
.code
.486
start:  mov ax,@data
    mov ds,ax
    mov cx,0C470h ;количество итераций цикла
    mov ax,12h   ;инициализация графического режима 640х480х16 цветов
    int 10h
;------------------------------------------------------------------
    finit ;инициализация сопроцессора 
    fldz
    mov ah,0Ch; функция 10h прерывания - установить точку
    xor bx,bx
l1: fld st
    fld st
    fsincos             
    fmul divK
    fmul st,st(2)
    fistp word ptr xr ;заносим X в переменную для вывода на экран 
    fmul divK
    fmul
    fistp word ptr yr ;заносим Y в переменную для вывода на экран 
    push cx
    mov cx,xr
    mov dx,yr
    add dx,240; 240 - половина высоты экрана в этом режиме
    add cx,320; 320 - половина ширины экрана в этом режиме
        mov al,0A3h; цвет правой спирали
    int 10h           ;выводим точку заданным цветом 
    sub dx,yr
    sub dx,yr
        mov al,0A4; цвет левой спирали
    int 10h           ;выводим точку заданным цветом 
    pop cx
    fadd delta;вычисляем новое значение alpha
    loop l1 ;цикл по cx
;--------------------------------------------------------------------
    mov ah,0 ;ожидание нажатия клавиши 
    int 16h
    mov ax,3 ;перевод обратно в TextMode
    int 10h
        mov ah,4Ch ;стандартный выход
    int 21h
end start
4
Ушел с форума
Автор FAQ
15840 / 7422 / 994
Регистрация: 11.11.2010
Сообщений: 13,385
30.05.2013, 05:04  [ТС] 4
Синус на ассемблере
Вычислить синус на ассемблере можно несколькими способами, выбирайте тот, который вам больше подходит:
  • Вычисление через встроенные функции FPU
    Радиан (от лат. radius - радиус) центральный угол, соответствует дуге, длина которой, равна ее радиусу. Содержит приблизительно 57°17'45", принимается за единицу измерения углов. Углы в 30°, 45°, 60°, 90° содержат в себе соответственно https://www.cyberforum.ru/cgi-bin/latex.cgi?\frac{\pi}{6}, https://www.cyberforum.ru/cgi-bin/latex.cgi?\frac{\pi}{4}, https://www.cyberforum.ru/cgi-bin/latex.cgi?\frac{\pi}{3}, https://www.cyberforum.ru/cgi-bin/latex.cgi?\frac{\pi}{2} радиан. Угол в 180°/n содержит https://www.cyberforum.ru/cgi-bin/latex.cgi?\frac{\pi}{n} радиан. Трансцендентные команды FPU работают с верхним элементом стека FPU. Команды работают с аргументом выраженным в радианах, поэтому для вычислений функций угла заданном в градусах следует вначале преобразовать это значение в радианы по формуле угол(радианы) = угол(градусы) https://www.cyberforum.ru/cgi-bin/latex.cgi?\frac{\pi}{180}
    • Синус fsin
      Команда FSIN заменяет величину в ST на ее синус. Единица измерения - радиан. Значение аргумента должно удовлетворять условию: |ST| ≤ 263. По поводу 263. Если значение аргумента выйдет за пределы этого числа, то значение ST не изменится — изменится только значение условного флага C2, которое установится в 1.
      Assembler
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      
      .data
      alpha dd 45.0;
      sin dd ?;sin(45)=0,70710678118654752440084436210485
      .code
           finit
           fld alpha;st(0)=45.0
           db 68h;push 3C8EFA35h
           dd 0.017453292519943295769236907684886;pi/180
              fmul dword ptr [esp];st(0)=0,78539816339744830961566084581988
          fsin;
          pop eax
          fstp sin
    • Синус fsincos
      Команда FSINCOS вычисляет одновременно значения синуса и косинуса параметра ST(0). Значение синуса записывается в ST(1), косинуса — в ST(0).
      Assembler
      1
      2
      3
      4
      5
      6
      7
      8
      
          finit
          fld alpha;st(0)=45.0
          push 3C8EFA35h;pi/180
          fmul dword ptr [esp];st(0)=0,78539816339744830961566084581988
          fsincos
          pop eax
          fxch st(1)
          fstp sin
  • Табличное вычисление синуса
    Из курса тригонометрии известно, что график синуса углов от 0 до 360° представляет собой кривую, изображенную на рис. Приближенные значения синуса угла X можно получить из формулы https://www.cyberforum.ru/cgi-bin/latex.cgi?Sin(x)=x-\frac{x^3}{3!}+\frac{x^5}{5!}-\frac{x^7}{7!}+... можно написать программу, выполняющую эти вычисления. Если в ваших приложениях требуется, чтобы значения синусов имели высокую точность можно написать программу вычисления синуса, но в большинстве случаев можно воспользоваться таблицей значений синусов углов. Для получения значения синуса любого угла от 0 до 360° достаточно получить значения синусов для углов от 0 до 90°. Синусы углов от 91 до 180° являются зеркальным отражением синусов для углов от 0 до 90°. Синусы углов от 181 до 270° являются негативным обращением синусов для углов от 0 до 90°. Синусы углов от 271 до 360° являются негативным обращением и зеркальным отражением синусов для углов от 0 до 90°.
    Положение угла преобразование
    от 0 до 90° Sin(X)
    от 91 до 180° Sin(180°-X)
    181 до 270° -Sin(X -180°)
    271 до 360° -Sin(360°-X)
    В процедуре FIND_SIN табличная функция используется для преобразования угла в синус. Функция получает значение угла от 0 до 360° в регистре EAX и возвращает значение синуса в регистре EBX. Амплитуды синусов хранятся в виде целых чисел в таблице SIN. Они должны быть разделены на 10000 перед использованием.
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    
    .data
    Sin dd 0,175,349,523,698,872; 0-5
    dd 1045,1219,1392,1564,1736; 6-10
    dd 1908,2079,2250,2419,2588; 11-15
    dd 2756,2924,3090,3256,3420; 16-20
    dd 3584,3746,3907,4067,4226; 21-25
    dd 4384,4540,4695,4848,5000; 26-30
    dd 5150,5299,5446,5592,5736; 31-35
    dd 5878,6018,6157,6293,6428; 36-40
    dd 6561,6691,6820,6947,7071; 41-45
    dd 7193,7313,7431,7547,7660; 46-50
    dd 7771,7880,7986,8090,8191; 51-55
    dd 8290,8387,8480,8572,8660; 56-60
    dd 8746,8829,8910,8988,9063; 61-65
    dd 9135,9205,9272,9336,9397; 66-70
    dd 9455,9511,9563,9613,9659; 71-75
    dd 9703,9744,9781,9816,9848; 76-80
    dd 9877,9903,9926,9945,9962; 81-85
    dd 9976,9986,9994,9998,10000;86-90
    .code
    proc find_sin
        push eax
        cmp eax,181
        jb a2
        sub eax,180
        cmp eax,91
        jb a1
        neg eax
        add eax,180
    a1: mov ebx,sin[eax*4]
        neg ebx
        jmp a4
    a2: cmp eax,91
        jb a3
        neg eax
        add eax,180
    a3: mov ebx,sin[eax*4]
    a4: pop eax
        retn
    endp find_sin
  • Вычисление синуса через FPTAN и FPREM
    Синус вычисляется с использованием FPTAN (нахождение частичного тангенса), а также командой FPREM (частичный остаток). Программа вычисляет и печатает синусы углов от 1/2 до 6 с шагом 1/2 радиана.
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    
    EXTRN FLOAT_ASCII:NEAR
    NUM_ANGLE DW 1
    DEN_ANGLE DW 2
    FOUR DW 4
    C3 EQU 40h
    C2 EQU 4
    C1 EQU 2
    C0 EQU 1
    ERROR_MSG DB 'Угол слишком большой', 10, 13, '$'
    SIN PROC 
                    finit                ;-----ST(0)-----;-----ST(1)------
    DO_AGAIN: fild NUM_ANGLE ;                 ;
                   fidiv DEN_ANGLE ; X = Угол       ;
                   fldpi            ; pi                   ; X
                   fidiv FOUR        ; pi/4                ; X
                   fxch               ; X                    ; pi/4
                   fprem              ; R                    ; pi/4
                   FSTSW AX
                   TEST AH, C2
                     JNZ BIG_ANGLE
                     TEST AH, C1 ; Определяется, необходимо ли вычитание pi/4
                     JZ DO_R ; Если 0, то не необходимо вычитание p/4
                      FSUBRP ST(1), ST(0) ; A = pi/4-R ; ?
                      JMP SHORT DO_FPTAN
    DO_R:            FXCH                    ; p/4     ; R
                        FCOMP                  ; R        ; ?
    DO_FPTAN:     FPTAN                  ; OPP     ; ADJ Где OPP/ADJ=Tan(A)
    ;----- Определение того, что нужно - синус или косинус
                      TEST AH, C3 or C1
                      JPE DO_SINE
                      FXCH                       ; ADJ     ; OPP
    DO_SINE:                                   ; D        ; N
    ;----- Вычисление N/SQR(N^2 + D^2)
                      FMUL ST(0)              ; D^2        ; N
                      FXCH ST(1)              ; N          ; D^2
                       FLD ST(0)               ; N           ; N ; D^2
                       FMUL ST(0)             ; N^2         ; N ; D^2
                       FADD ST(2)             ; N^2 + D^2  ; N ; D^2
                       FSQRT                    ; SQR(N^2 + D^2) ; N ; D^2
                       FDIVRP ST(1)           ; SIN(X)   ; D^2
                       FXCH ST(1) ; D2 ; SIN(X)
                       FCOMP ; SIN(X) ; ?
                       TEST AH, C0
                        JZ SIGN_OK
                        FCHS
    SIGN_OK:       CALL FLOAT_ASCII
                        INC NUM_ANGLE
                        CMP NUM_ANGLE, 13
                        JNA DO_AGAIN
    RETURN_INST: RET
    BIG_ANGLE:    mov DX, offset ERROR_MSG
                       MOV AH, 9
                       INT 21h
                       RET
    SIN ENDP
    4.79425539E-001
    8.41470985E-001
    9.97494987E-001
    5.98472144E-001
    1.41120008E-001
    -3.50783228E-001
    -7.56802495E-001
    -9.77530118E-001
    -9.58924275E-001
    -7.05540326E-001
    -2.79415498E-001
    2.15119988E-001
    сопроцессор загружает два целых числа и делит их, формируя исходный угол.
    Синус — периодическая функция. То есть функция дает один и тот же результат в случае исходных чисел, различающихся ровно на https://www.cyberforum.ru/cgi-bin/latex.cgi?2\pi. Поэтому первой задачей является замена исходного угла соответствующим значением, лежащим в диапазоне https://www.cyberforum.ru/cgi-bin/latex.cgi?0 \leq X <\pi Для FPTAN требуется, чтобы угол находился в диапазоне https://www.cyberforum.ru/cgi-bin/latex.cgi?0 \leq X < \pi/4 Это означает, что даже если угол и меньше https://www.cyberforum.ru/cgi-bin/latex.cgi?\pi, мы должны уменьшить его еще, чтобы он удовлетворял ограничениям команды FPTAN. Если исходный угол уменьшен до значения, меньшего https://www.cyberforum.ru/cgi-bin/latex.cgi?\pi/4, все еще можно определить верное значение тригонометрических функций. Чтобы это сделать, надо знать, в каком месте исходного диапазона от 0 до https://www.cyberforum.ru/cgi-bin/latex.cgi?\pi находился исходный угол. Нужное уменьшение угла выполняет команда FPREM. Она не только вычисляет остаток, но и три младших бита частного, определяемого в течение процесса поиска остатка. Эти три бита команда записывает в слово состояния. Следовательно, хотя мы и уменьшили угол до значения одной восьмой исходного диапазона, все же можно определить октант, в который попадет угол. Зная его, можно найти формулу вычисления синуса с помощью тригонометрических преобразований. Таблица показывает связь между исходным октантом и методом вычисления синуса угла. В таблице предполагается, что число R — это остаток от уменьшения исходного угла до значения меньше https://www.cyberforum.ru/cgi-bin/latex.cgi?\pi/4. Номер октанта появляется в разрядах C3, C1, C0 после выполнения команды FPREM. С помощью этой таблицы можно определить формулу вычислений, применяемую в каждом случае выполнения программы. После загрузки значения угла в радианах программа загружает число и делит его на 4, чтобы использовать в команде FPREM. В этот момент "захватывается" слово состояния. Если процесс поиска остатка не завершился на этом единственном шаге, это означает, что исходный угол был больше 264. Следовательно, его значение настолько больше максимально возможного при вычислениях тригонометрических функций, что мы отбрасываем это число, как слишком большое. Этого не происходит со значениями, выбранными в примере, но здесь для иллюстрации введена такая проверка.
    C0 C3 C1Диапазон SIN(X) =
    0 0 0 от 0 до https://www.cyberforum.ru/cgi-bin/latex.cgi?\pi/4 SIN(R)
    0 0 1 от https://www.cyberforum.ru/cgi-bin/latex.cgi?\pi/4 до https://www.cyberforum.ru/cgi-bin/latex.cgi?\pi/2 COS(https://www.cyberforum.ru/cgi-bin/latex.cgi?\pi/4-R)
    0 1 0 от https://www.cyberforum.ru/cgi-bin/latex.cgi?\pi/2 до https://www.cyberforum.ru/cgi-bin/latex.cgi?3\pi/4 COS(R)
    0 1 1 от https://www.cyberforum.ru/cgi-bin/latex.cgi?3\pi/4 до https://www.cyberforum.ru/cgi-bin/latex.cgi?\pi SIN(https://www.cyberforum.ru/cgi-bin/latex.cgi?\pi/4-R)
    1 0 0 от https://www.cyberforum.ru/cgi-bin/latex.cgi?\pi до https://www.cyberforum.ru/cgi-bin/latex.cgi?5\pi/4 -SIN(R)
    1 0 1 от https://www.cyberforum.ru/cgi-bin/latex.cgi?5\pi/4 до https://www.cyberforum.ru/cgi-bin/latex.cgi?3\pi/2 -COS(https://www.cyberforum.ru/cgi-bin/latex.cgi?\pi/4-R)
    1 1 0 от https://www.cyberforum.ru/cgi-bin/latex.cgi?3\pi/2 до https://www.cyberforum.ru/cgi-bin/latex.cgi?7\pi/4 -COS(R)
    1 1 1 от https://www.cyberforum.ru/cgi-bin/latex.cgi?7\pi/4 до https://www.cyberforum.ru/cgi-bin/latex.cgi?2\pi -SIN(https://www.cyberforum.ru/cgi-bin/latex.cgi?\pi/4-R)
    (https://www.cyberforum.ru/cgi-bin/latex.cgi?R - остаток, https://www.cyberforum.ru/cgi-bin/latex.cgi?0<R<\pi/4)
    Программа проверяет разряд C1 в регистре состояния, чтобы определить, должна ли она использовать остаток R, или его надо вычесть из https://www.cyberforum.ru/cgi-bin/latex.cgi?\pi/4. Так как https://www.cyberforum.ru/cgi-bin/latex.cgi?\pi/4 еще находится в одном из регистров, это сделать просто. Если вычитание не требуется, команда FCOMP удаляет из стека ненужное значение https://www.cyberforum.ru/cgi-bin/latex.cgi?\pi/4. Затем команда FPTAN вычисляет частичный тангенс. Результат работы команды показан, как OPP/ADJ (Opposite - противоположный, Adjacent - соседний), что равно тангенсу угла R или https://www.cyberforum.ru/cgi-bin/latex.cgi?\pi/4-R, в зависимости от того, что было выбрано. С помощью этих двух чисел теперь можно определить синус или косинус угла.
    Например, синус, заданный парой чисел OPP/ADJ, можно вычислить по формуле SIN(X) = OPP/SQR(OPP2+ADJ2), где TAN(X) = OPP/ADJ
    Чтобы вычислить косинус, нужно числитель заменить на ADJ. Мы решаем, нужен ли синус или косинус, анализируя запомненные описатели октанта, то есть, проверяя значения разрядов C3 и C1. Команда TEST выделяет эти значения, а команда JPE делает переход, если они оба нулевые или оба единичные. В этом случае мы вычисляем синус; если же они различны, мы вычисляем косинус, что достигается заменой местами значений OPP и ADJ в стеке регистров. Следующие команды вычисляют значение синуса (или косинуса) по значению частичного тангенса. Единственный шаг, который надо выполнить - это определение окончательного знака результата. В случае синуса результат отрицателен, если угол находится в октантах от четвертого до седьмого. Проверка разряда C0 определяет верный знак результата. Затем программа FLOAT_ASCII, печатает число в плавающем формате. Управление возвращается назад, к началу цикла, если еще не пройдены все октанты
  • Вычисление функции y = sin(x) разложением в ряд Маклорена
    Одним из распространенных методов вычисления значений конкретной функции является метод разложения ее в ряд Тейлора или его частный случай — ряд Маклорена. Точность такого метода зависит от числа членов ряда, взятых для вычисления требуемой функции. Представление функции y = sin(x) рядом Маклорена в точке разложения х0 = 0 имеет следующий вид
    https://www.cyberforum.ru/cgi-bin/latex.cgi?y = sin(x)\approx \sum_{i = 0}^{\infty}(-1)^{i} \times \frac{x^{2i+1}}{(2i + 1)!}
    Для учебных и бытовых расчетов разложение до четвертого члена ряда https://www.cyberforum.ru/cgi-bin/latex.cgi?sin(x) \approx x - \frac{x^3}{6}+\frac{x^5}{120}-\frac{x^7}{5040} дает достаточно хорошее приближение к функции синуса для значений от https://www.cyberforum.ru/cgi-bin/latex.cgi?-\pi до https://www.cyberforum.ru/cgi-bin/latex.cgi?+\pi радиан. После несложных преобразований получим:
    https://www.cyberforum.ru/cgi-bin/latex.cgi?sin(x) \approx x - \frac{x^3}{6}+\frac{x^5}{120}-\frac{x^7}{5040}=x(1-\frac{x^2}{6}(1-\frac{x^2}{20}(1-\frac{x^2}{42})))=\frac{x(5040-x^{2}(840-x^{2}(42-x^{2})))}{5040}
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
        mov eax,x
        imul eax   
        mov ecx,eax; ecx=x^2
        sub eax,42  ;eax = -(42 - x^2) 
        imul ecx   ;eax = -x^2(42 - x^2)
        add eax,840;eax = 840-x^2(42 - x^2)
        imul ecx   ;eax = x^2(840-x^2(42 - x^2))
        mov ebx,-5040;ebx= -5040
        add eax,ebx;eax = -(5040-x^2(840-x^2(42 - x^2)))
        imul x    ;edx:eax=-x(5040-x^2(840-x^2(42 - x^2)))
        idiv ebx   ;eax=x(5040-x^2(840-x^2(42 - x^2)))/5040
        mov y,eax
    ну или так
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    
    .data
    x dq 0.78539816339744830961566084581988; pi/4
    eps dq 0.00000001; погрешность
    sint dq ?
    i    dd 2
    .code
    start:  finit
        fld x
        fmul st,st;st(0)=x*х
        fld x
            fld st
    a0: fabs
        fcom eps  ;|N| < Eps ?
        fstsw ax
        sahf
        jb exit
        fmul st,st(2); N*x*x
        test i,2     ; менять знак или нет?
        jz a1
        fchs
    a1: fidiv i      ; N*x*x/i
        inc i
        fidiv i      ; N*x*x/(i*(i+1))
        inc i        ; i = i + 2
        fadd st(1),st; SinT = SinT + N*x*x/(i*(i+1))
        jmp a0
    exit:   fxch st(1)
        fstp sint
3
Ушел с форума
Автор FAQ
15840 / 7422 / 994
Регистрация: 11.11.2010
Сообщений: 13,385
30.05.2013, 06:06  [ТС] 5
DEBUG.EXE
статья winsoft
взято здесь
Стандартный отладчик debug.exe, входит в любую версию DOS/Windows. debug.exe прост, доступен и подходит для начинающих программистов на языке Ассемблер.
Для запуска debug.exe заходим в Пуск->Выполнить и набираем команду debug.

Команды debug.exe

Для начала разберемся с правилами набора команд debug.exe:
  • debug.exe не различает регистр букв.
  • Пробелы в командах используется только для разделения параметров.
  • Вводимые числа должны быть в шестнадцатеричной системе счисления, причем без завершающей буквы h.
  • Сегмент и смещение записываются с использованием двоеточия, в формате сегмент:смещение, например, CS:3C1 (смещение 3C1h в сегменте кода) или 40:17 (смещение 17h в сегменте, адрес начала которого - 40[0]h).

Разобравшись с правилами, переходим к изучению команд debug.exe. Замечу, что работа с командами debug.exe в чем-то похожа на работу с командной строкой DOS. После загрузки отладчика на экране появится приглашение, выглядящее в виде дефиса:
Код
-_
Регистры CS, DS, ES, SS в этот момент инициализированы адресом 256-байтного префикса сегмента програмы, а рабочая области в памяти будет начинаться с адреса этого префикса + 100h.
Команды debug.exe вводятся сразу после приглашения на месте, которое отмечено курсором. Каждая команда состоит из идентификатора и параметров, идентификатор состоит из одной буквы.
таблица команд debug.exe
Команда Описание Формат
A (Assemble)Транслирование команд ассемблера в машинный код; адрес по умолчанию - CS:0100h.A [<адрес_начала_кода>]
C (Compare)Сравнение содержимого двух областей памяти; по умолчанию используется DS. В команде указывается либо длина участков, либо диапазон адресов. C <начальный_адрес_1> L<длина> <начальный_адрес_2> C <начальный_адрес_1> <конечный_адрес_1> <начальный_адрес_2>
D (Display/Dump)Вывод содержимого области памяти в шестнадцатеричном и ASCII-форматах. По умолчанию используется DS; можно указывать длину или диапазон. D [<начальный_адрес> [L<длина>]] D [начальный_адрес конечный_адрес]
E (Enter)Ввод в память данные или инструкции машинного кода; по умолчанию используется DS.E [<адрес> [<инструкции/данные>]]
F (Fill)Заполнение области памяти данными из списка; по умолчанию используется DS. Использовать можно как длину, так и диапазон. F <начальный_адрес_1> L<длина> '<данные>' F <начальный_адрес> <конечный_адрес> '<данные>'
G (Go)Выполнение отлаженной программы на машинном языке до указанной точки останова; по умолчанию используется CS. При этом убедитесь, что IP содержит корректный адрес.G [=<начальный_адрес>] <адрес_останова> [<адрес_останова> ...]
H (Hexadecimal)Вычисление суммы и разности двух шестнадцатеричных величин.H <величина_1> <величина_2>
I (Input)Считывание и вывод одного байта из порта.I <адрес_порта>
L (Load)Загрузка файла или данных из секторов диска в память; по умолчанию - CS:100h. Файл можно указать с помощью команды N или аргумента при запуске debug.exe.
L [<адрес_в_памяти_для_загрузки>]
L [<адрес_в_памяти_для_загрузки> [<номер_диска> <начальный_сектор> <количество_секторов>]]
M (Move)Копирование содержимого ячеек памяти; по умолчанию используется DS. Можно указывать как длину, так и диапазон.
M <начальный_адрес> L<длина> <адрес_назначения>
M <начальный_адрес> <конечный_адрес> <адрес_назначения>
N (Name)Указание имени файла для команд L и W.N <имя_файла>
O (Output)Отсылка байта в порт.O <адрес_порта> <байт>
P (Proceed)Выполнение инструкций CALL, LOOP, INT или повторяемой строковой инструкции с префиксами REPnn, переходя к следующей инструкции.P [=<адрес_начала>] [<количество_инструкций>]
Q (Quit)Завершение работы debug.exe.Q
R (Register)Вывод содержимого регистров и следующей инструкции.R <имя_регистра>
S (Search)Поиск в памяти символов из списка; по умолчанию используется DS. Можно указывать как длину, так и диапазон.
S <начальный_адрес> L<длина> '<данные>'
S <начальный_адрес> <конечный_адрес> '<данные>'
T (Trace)Пошаговое выполнение программы. Как и в команде P, по умолчанию используется пара CS:IP. Замечу, что для выполнения прерываний лучше пользоваться командой P.T [=<адрес_начала>] [<количество_выполняемых_команд>]
U (Unassemble)Дизассемблирование машинного кода; по умолчанию используется пара CS:IP. К сожалению, debug.exe некорректно дизассемблирует специфические команды процессоров 80286+, хотя они все равно выполняются корректно.
U [<начальный_адрес>]
U [<начальный_адрес конечный_адрес>]
W (Write)Запись файла из debug.exe; необходимо обязательно задать имя файла командой N, если он не был загружен. А программы записываются только в виде файлов .COM!W [<адрес> [<номер_диска> <начальный_сектор> <количество_секторов>]]
Примечание. Символами [ ] отмечены необязательные параметры.
Просмотр областей памяти
В этой части рассмотрена работа команды D, позволяющей просматривать содержимое отдельных областей памяти.
Этот пример использует команду D для просмотра области памяти, начиная с 0159:0240:
Код
-d 0159:0240
0159:0240  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ..........l.....
0159:0250  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0159:0260  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0159:0270  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00   ................
0159:0280  00 00 00 00 00 00 00 00-00 FF FF FF FF 00 00 00   ................
0159:0290  FF 00 00 00 00 00 00 00-00 00 4E 4F 20 4E 41 4D   ..........NO NAM
0159:02A0  45 20 20 20 20 00 26 81-4F 03 00 01 CB 00 00 00   E    .&.O.......
0159:02B0  00 00 00 00 00 00 00 00-00 00 00 01 07 04 FF 02   ................
-_
Здесь на запрос просмотра участка памяти мы получили восемь строк, в которых указано содержимое выбранной области памяти. Каждая строка состоит из трех частей:
  • Адрес первого слева показанного байта в формате сегмент:смещение.
  • Шестнадцатеричное представление параграфа (16 байт), начинающегося с указанного в начале строки байта.
  • Символы этого же параграфа в ASCII-формате.

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

Полезные приемы с командой D
  • Проверка параллельных и последовательных портов
    Первые 16 байт области данных BIOS содержат адреса параллельных и последовательных портов. Поэтому с помощью следующей команды можно проверить эти порты:
    Код
    -D 40:00_
    Первые выведенные восемь байтов указывают на адреса последовательных портов COM1-COM4. Следующие 8 байтов указывают на адреса параллельных портов LPT1-LPT4.
    Например, если на вашем компьютере есть один параллельный порт, то первые два байта будут, скорее всего, такими: 7803. Адрес порта записывается в обращенной последовательности, т.е. 0378.
  • Проверка оборудования
    Первые два байта, располагающиеся в BIOS по адресу 410h, содержат информацию об установленном в системе оборудовании. Находим эти байты командой:
    Код
    -D 40:10_
    Предположим, что первые два байта окажутся 23 44. Расшифруем эти байты для получения информации об установленных устройствах. Для этого обратим эти байты (44 23), затем переведем их в двоичную систему счисления. Получаем:
    Значение бита0100010000100011
    Позиция бита15141312111009080706050403020100
    Что означают эти биты? Продолжаем расшифровывать:
    БитыУстройство
    15, 14Число параллельных портов (01 = 1 порт, ...)
    11, 10, 9Число последовательных портов (..., 010 = 2 порта, ...)
    7, 6Число дисководов (00 = 1 дисковод, 01 = 2, 10 = 3, 11 = 4)
    5, 4Начальный видеорежим (01 = 40х25 цветной, 10 = 80х25 цветной, 11 = 80х25 монохромный)
    1Присутствие математического сопроцессора (0 = нет, 1 = есть)
    0Наличие привода для дискет (0 = нет, 1 = есть)
  • Проверка состояния регистра клавиатуры
    В области данных BIOS по адресу 417h находится первый байт, который хранит состояние регистра клавиатуры. Выключаем Num Lock и Caps Lock, затем набираем команду:
    Код
    -d 40:17_
    Первый байт будет равен 00. Включив Num Lock и Caps Lock, снова выполняем команду. Теперь первый байт должен равняться 60. Опытным путем установлено, что при включенном Num Lock первый байт равен 20, а при Caps Lock - 40.
  • Проверка состояния видеосистемы
    По адресу 449h в BIOS находится первая область видеоданных. Для проверки набираем:
    Код
    -d 40:49_
    Первый байт показывает текущий видеорежим (к примеру, 03 - цветной), а второй - число столбцов (например, 50 - режим с 80 столбцами). Число строк можно найти по адресу 484h (40:84).
  • Проверка копирайта BIOS и серийного номера
    Сведения об авторских правах на BIOS встроены в ROM BIOS по адресу FE00:0. Строку с копирайтом можно легко найти в ASCII-последовательности, а серийный номер - в виде шестнадцатеричного числа. Хотя, строка с указанием авторских прав может быть длинной и не умещаться в выведенную область памяти. В таком случае следует просто ввести еще раз D.
  • Проверка даты выпуска BIOS
    Эта дата также записана в ROM BIOS начиная с адреса FFFF:5. После выполнения соответствующей команды в ASCII-последовательности будет находиться эта дата, записанная в формате мм/дд/гг.
    Пример:
    Код
    -d FFFF:5
    FFFF:0000                 31 31 2F-32 38 2F 30 35 00 FC 00        11/28/05...
    FFFF:0010  34 12 00 00 00 00 00 00-00 00 00 00 00 00 00 00   4...............
    ...
Непосредственный ввод программы в память с помощью debug.exe
debug.exe позволяет вводить программу непосредственно в память машины, а затем следить и управлять ее выполнением. Мы будем вводить программу в машинных кодах, используя команду E. При этом будьте бдительны - ввод ошибочных данных по ошибочному адресу чреват непредсказуемыми последствиями! Хотя к серьезным проблемам в системе это вряд ли приведет, но потерять все данные, введенные в debug.exe, можно потерять запросто.
Программа, которую мы сейчас будем вводить, использует данные, заложенные непосредственно в теле инструкций. Далее показан листинг программы на Ассемблере, в комментариях указаны аналоги команд языка в машинных кодах, а также объяснение каждой команды. Замечу, что в числах нет символа h, поскольку, как было сказано выше, debug.exe понимает только числа в шестнадцатеричной системе.
Код
MOV AX, 0123 ; код B82301: заносим значение 0123h в AX
ADD AX, 0025 ; код 052500: прибавляем 0225h к значению AX
MOV BX, AX   ; код 8BD8: заносим значение AX в BX
ADD BX, AX   ; код 03D8: прибавляем значение AX к BX
MOV CX, BX   ; код 8BCB: заносим значение BX в CX
SUB CX, AX   ; код 2BC8: отнимаем значение AX из CX
SUB AX, AX   ; код 2BC0: очищаем AX
JMP 100      ; код EBEE: переходим к началу программы
Как можно заметить, каждая машинная инструкция имеет длину от 1 до 3 байтов. Первый байт указывает операцию, последующие - ее операнды. Исполнение программы начинается соответственно с первой инструкции и последовательно проходит через все инструкции одну за другой.
Теперь можно ввести программу в память. Разделим машинный код на три части по шесть байт и введем каждую, используя команду E и начиная с адреса CS:100.
Код
-E CS:100 B8 23 01 05 25 00
-E CS:106 8B D8 03 D8 8B CB
-E CS:10C 2B C8 2B C0 EB EE
-_
Теперь, когда программа введена в память, попробуем управлять ее выполнением. Для начала проверим текущее состояние регистров и флагов, для этого вводим команду R. Отладчик выведет содержимое регистров в шестнадцатеричной форме; на разных машинах содержимое регистров может различаться.
Код
-r
AX=0000  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=15D7  ES=15D7  SS=15D7  CS=15D7  IP=0100   NV UP EI PL NZ NA PO NC
15D7:0100 B82301        MOV     AX,0123
-_
Итак, как можно видеть, debug.exe инициализировал сегменты DS, ES, SS, CS одним и тем же адресом. Регистр IP содержит 0100, указывая на то, что инструкции выполняются со смещения 100h относительно CS (а мы, вводя инструкции в память, как раз указали этот адрес).
Здесь же указаны и значения флагов переполнения, направления, прерывания, знака, нуля, дополнительного переноса, четности и переноса:
ЗначениеОписание
NV Отсутствие переполнения
UP Направление вверх или вправо
EI Разрешение прерываний
PL Положительный знак
NZ Ненулевое значение
NA Отсутствие дополнительного переноса
PO Нечетное слово
NC Отсутствие переноса
После регистров и состояния флагов debug.exe выводит информацию о первой инструкции, которая будет выполняться:
  • Адрес инструкции, в нашем случае это 15D7:0100, где 15D7 - адрес сегмента кода.
  • Машинный код, соответствующей этой инструкции (B82301).
  • Собственно инструкция, записанная на ассемблере (MOV AX,0123).
Теперь, после анализа содержимого регистров и флагов, давайте перейдем к выполнению программу. Выполнять программу мы будем по-шагово, используя команду T. Использовав в первый раз команду T, мы выполняем инструкцию MOV. Здесь машинный код операнда инструкции - 2301. Операция помещает 23 в AL (младшая половина AX), а 01 - в AH (старшая).
После этого debug.exe снова выводит информацию о регистрах:
Код
-t
AX=0123  BX=0000  CX=0000  DX=0000  SP=FFEE  BP=0000  SI=0000  DI=0000
DS=15D7  ES=15D7  SS=15D7  CS=15D7  IP=0103   NV UP EI PL NZ NA PO NC
15D7:0100 B82301        ADD     AX,0025
-_
Теперь AX содержит 0123h, IP - 0103h (следовательно, длина выполненной инструкции: 0103h - 0100h = 3 байта), а в качестве следующей инструкции указана операция ADD.
Так, раз за разом выполняя команду T, мы дойдем до последней инструкции JMP 100. Она установит регистр IP в 100h, и debug.exe вернется к началу программы. Возвращаясь к началу программы, следует заметить, что в DS, ES, SS и CS содержится один и тот же адрес. Дело в том, что debug.exe рассматривает введенные программы исключительно как программы .COM. А в программах .COM, в отличие от .EXE, стек, код и данные хранятся в одном сегменте.
Ассемблирование и дизассемблирование
В прошлом примере мы вводили программу в машинных кодах, однако, debug.exe вполне способен понимать инструкции, записанные на ассемблере. Для работы с такими программами в debug.exe используются команды A и U.
Команда A запрашивает инструкции на ассемблере и преобразовывает их в машинный код. Для начала инициализируем начальный адрес для ввода инструкций (100h):
Код
-a 100_
Отладчик выведет адрес сегмента кода и смещения (например, 13F2:0100). Теперь мы должны ввести следующие инструкции на ассемблере в память, после каждой строки нажимая Enter:
Код
MOV CL, 42
MOV DL, 2A
ADD CL, DL
JMP 100
После ввода последней инструкции нажимаем Enter дважды, чтобы указать отладчику, что мы закончили вводить текст программы. Теперь программу можно запускать, используя команды R для просмотра регистров и T для трассировки. Замечу, что в своих программах при наличии инструкций INT их следует обрабатывать не командой T, а командой P, которая обрабатывает все прерывание сразу.
Перейдем к процедуре дизассемблирования, а в качестве примера возьмем только что введенную программу. Используем адреса первой и последней инструкций для указания диапазона, который мы собираемся дизассемблировать, т.е. 100h и 107h.
Код
-u 100, 107_
После выполнения этой команды debug.exe выведет инструкции, находящиеся в указанном диапазоне, на ассемблере, в машинных кодах, а также адрес каждой инструкции:
Код
13F2:0100 B142          MOV     CL, 42
13F2:0102 B22A          MOV     DL, 2A
13F2:0104 00D1          ADD     CL, DL
13F2:0106 EBF8          JMP     0100
Итог
А теперь, после небольшого обзора возможностей стандартного отладчика debug.exe давайте подведем итоги. Итак:
  • debug.exe можно применять для наблюдений и отладки программ на ассемблере и машинных кодах.
  • debug.exe позволяет трассировать программу, устанавливать точки останова, просматривать области памяти, вводить программы непосредственно в память компьютера.
  • debug.exe представляет загружаемые программы как программы .COM.
  • debug.exe воспринимает только числа в шестнадцатеричной системе.
  • debug.exe не различает регистр букв.
Фрагменты из статьи
"низкоуровневое программирование для дZенствующих - DZebug:
руководство юZверя"
  • Запуск debug.exe
    два способа запуска DEBUG с файлом.
    1. С командной строки:
      Код
      debug имя_файла.тип <Enter>
    2. Без командной строки:
      Код
      debug
      получаем приглашение debug.exe в виде черточки "-".
      Далее следуют команды:
      Код
      -n имя_файла.тип <Enter> 
      -l
      debug.exe загрузил вашу программу и готов к работе
  • Отображение и изменение значений регистров
    Первым делом мы просмотрим содержимое регистров, используя команду R. В качестве объекта извращения (Serrgio сегодня будет извращаться несколько иначе) - ваша же прога из #7 п. 4...

    Если вы ввели R без параметров, значения регистров будут выведены примерно так:
    Код
    AX=0000 BX=0000 CX=0043 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000 
    DS=16BB ES=16BB SS=16BB CS=16BB IP=0100 NV UP DI PL NZ NA PO NC 
    15A3:0100 30C0  XOR AL,AL
    CX содержит длину файла (0043h или 67d). Если размер файла превышает 64К, то BX будет содержать старшую часть размера файла. Это очень важно знать при использовании команды Write - размер файла содержится именно в этих регистрах.

    Запомните: когда файл находится в памяти, debug.exe не знает его размер. При записи данные о размере берутся из регистров CX и BX. Страшно даже подумать, что может произойти, если мы по ошибке введем команду Write, а в это время BX и CX будут содержать FFFF! Страшно, но приятно...

    Если мы хотим изменить значение одного из регистров, мы вводим R и имя регистра. Давайте поместим в AX слово FUCK
    Код
    -R AX
    Вывалится:
    Код
    AX 0000 
    :
    ":" - это приглашение ввести новое значение. Мы отвечаем FUCK
    Код
    :FUCK
    Вывалится:
    Код
    ^Error
    debug выдал сообщение об ошибке.
    Тут дело даже не в том, что debug.exe не понравилось слово, которое мы ввели. Ему в сущности глубоко наплевать, какое значение мы хотим занести в регистр, главное - чтобы цифры были шестнадцатеричные. И если F является таковой, то U - нет. Debug.exe заботливо указал нам на нее значком " ^ ".
    Ну и бог с ним. Попробуем ввести что-нибудь более пристойное, например D3E0:
    Код
    -R AX 
    AX 0000 
    :D3E0
    Теперь, если мы просмотрим регистры, мы увидим следующее:
    Код
    AX=D3E0 BX=0000 CX=0043 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000 
    DS=16BB ES=16BB SS=16BB CS=16BB IP=0100 NV UP DI PL NZ NA PO NC 
    15A3:0100 30C0  XOR AL,AL
    Вы увидите, что ничего не изменилось кроме регистра AX. Ему присвоили новое значение, как мы помним.
    Еще один важный момент: команда Register может использоваться только для 16-битных регистров (AX, BX и так далее). Она не может изменять значения 8-битных регистров (AH, AL, BH и так далее). Например, чтобы изменить AH, вы должны ввести новое значение в регистр AX с новым AH и старым значением AL.
  • ДАМП ПАМЯТИ

    Если вы не очень хорошо умеете читать машинные команды процессора, команду Dump можно использовать для вывода на экран данных (тексты, флаги и т. д.). Для вывода кода лучше использовать команду Unassemble.

    Если мы теперь введем команду Dump, debug.exe определит начало программы. Для этого он использует регистр DS, и, так как это COM-файл, начинает вывод с адреса DS:0100. Он выведет 80h (128d) байт данных (или то количество, которое вы сами определите)... Следующее употребление команды Dump отобразит следующие 80h байт и так далее.

    Например, первая команда Dump выведет 80h байт начиная с адреса DS:0100, вторая команда выведет 80h байт начиная с адреса DS:0180...

    Конечно, можно самому определять нужный вам сегмент и смещение при использовании Dump, только нужно использовать для определения только шестнадцатеричные цифры.

    Например, запись D DS:BX не верна. Загрузив нашу программу и введя команду Dump, мы увидим очень непонятные буквы и цифры.

    Вы наверное уже знаете, что эти буквы и цифры - не что иное как наша программа. Невероятно, но это так.

    Вы видите, что выводимые командой Dump данные разделены на три части.

    Самая левая содержит адрес первого байта в строчке. Ее формат - сегмент:смещение.

    Следующая колонка содержит шестнадцатеричные данные по указанному адресу. Каждая строчка содержит 16 байт данных.

    Третья колонка - это ASCII представление данных. Отображаются только стандартные символы ASCII. Специальные символы IBMPC не отображаются, вместо них выводятся точки ".". Это делает поиск простого текста более удобным.

    Dump не может выводить данные, выходящие за границы сегмента. Например, команда
    Код
    -D 0100 L F000 <Enter>
    правильная (выводятся байты начиная с DS:0100 до DS:F0FF), а команда
    Код
    -D 9000 L 8000 <Enter>
    некорректна (8000h +9000h = 11000h - выходит за границу сегмента).

    Так как 64К - это 10000h то невозможно задать этот адрес четырьмя шестнадцатеричными цифрами, поэтому DEBUG использует 0000 для задания 10000h.

    Чтобы вывести на экран весь сегмент - введите
    Код
     -D 0 L 0.
  • ПОИСК БАЙТОВ

    Search служит для поиска заданного байта или последовательности байт в пределах сегмента. Параметры задания адреса точно такие, как для команды Dump, поэтому мы не будем здесь опять о них рассказывать.
    Еще мы должны задать данные, которые нужно искать. Они могут быть введены как в шестнадцатеричном, так и в символьном формате. Шестнадцатеричные данные вводятся как байты, через пробел или запятую. Символьные данные должны быть заключены в одинарные или двойные кавычки.
    Шестнадцатеричные и символьные данные могут чередоваться в любом порядке, например команда
    Код
     -S 0 L 100 12 34 'abc' 56 <Enter>
    - правильная, в результате произойдет поиск от DS:0000 до DS:00FF последовательности 12h 34h a b c 56h.
    Символы верхнего регистра отличаются от символов нижнего регистра. Например, 'ABC'- это не то же самое, что 'Abc' или 'abc' или другие комбинации верхних и нижних регистров. Однако, 'ABC' и "ABC" считаются одинаковыми, потому что кавычки - это всего лишь разделитель.
    Попробуем найти слово 'пИво'. У нас получится следующее:
    Код
         -S 0 L 0 'пИво' <Enter>
         -
    Ну нет в этой программе пИва!
    Попробуем пИво поискать в другом месте, а в нашей программе лучше поищем слово B7 20 B5 06:
    Код
         -S 0 L 0 B7 20 B5 06 <Enter>
         15A3:0110 
         -
    Итак, по адресу 15A3:0170 debug.exe нашел слово B7 20 B5 06.
    Опять же: значение сегмента у вас может быть иным, но смещение должно быть такое же...
    Если мы выведем данные на экран, то по этому адресу мы найдем строку '. ......=...0..'. Мы можем попробовать найти строчку '_¶¦_:-', или еще какую-нибудь нездоровую последовательность символов.
    Если мы захотим найти все места, где употребляется команда int 10h (в машинном коде эта команда имеет вид CD 10), мы сделаем следующее:
    Код
         -S 0 L 0 cd 10 <Enter>
    и получим длинную простыню:
    Код
         15A3:010E 
         15A3:011A 
         15A3:0126 
         15A3:0132 
         15A3:013E 
         15A3:0AE6 
         15A3:0F23 
         -
    Debug нашел последовательность CD 10 по этим адресам. Это не значит, что все CD 10 - это команды int 10h. Просто по указанным адресам расположены искомые данные. Это может быть (чаще всего) инструкция, но это также может быть и адрес, вторая часть инструкции JMP и т.д. Вы должны внимательно изучить код программы на данном участке памяти, чтобы быть уверенным, что это действительно int 10h.
    Вы ведь не думаете, что компьютер все сделает за вас? Конечно, компьютеры заменили человека во многих сферах деятельности, преимущественно там, где нужно работать нижними полушариями мозга, а там, где нужны еще и верхние полушария, без человека ну никак не обойтись! И это - звучит гордо!
  • Сравнение участков памяти
    Архиполезнейшая штука! .
    Команда сompare берет два заданных участка памяти и сравнивает их, байт за байтом. Если два адреса содержат разную информацию, они выводятся на экран вместе с их содержимым. Для примера мы сравним 2 байта DS:0100 с DS:0200 .
    Код
       -С 0100 L 2 0200 
       15A3:0100 30 0E 15A3:0200 
       15A3:0101 C0 00 15A3:0201
    Все 2 байта различны, поэтому они все выведены на экран. Если какие-нибудь байты окажутся одинаковыми, то они не будут выводится на экран. Если две области окажутся полностью одинаковыми, DEBUG просто ответит новым приглашением. Это очень удобный способ сравнения данных из памяти с данными из файла или ROM-BIOS
  • Дизассемблирование
    Unassemble - основная команда, которую вы будете использовать при отладке. Эта команда берет машинный код и преобразует его в инструкции ассемблера. Способ задания адреса такой же, как и в предыдущих командах, с одной лишь разницей: поскольку мы теперь будем работать с кодом (предыдущие команды в основном предназначены для работы с данными), регистр по умолчанию - CS. В .COM программах это делает небольшое отличие, если только вы сами не очистите DS. Однако в .EXE файлах это черевато трудностями, потому что изначально регистрам CS и DS присвоены разные значения.
    Код
         -u 
         15A3:0100 XOR AL,AL 
         15A3:0102 MOV BH,10 
         15A3:0104 MOV CH,05 
         15A3:0106 MOV CL,10 
         15A3:0108 MOV DH,10 
         15A3:010A MOV DL,3E 
         15A3:010C MOV AH,06 
         15A3:010E INT 10 
         15A3:0110 MOV BH,20 
         15A3:0112 MOV CH,06 
         15A3:0114 MOV CL,11 
         15A3:0116 MOV DH,0F 
         15A3:0118 MOV DL,3D 
         15A3:011A INT 10 
         15A3:011C MOV BH,30 
         15A3:011E MOV CH,07
    Мы видим уже знакомую нам программу. Если мы опять введем "u", то debug.exe выдаст нам очередную порцию кода. В нашем случае все обошлось благополучно, но бывает и так, когда вы не знаете, что вы в данный момент дизассемблируете - действительно код программы, или же ее данные. Debug.exe сделает все, что вы ему прикажете. Если вы скажете дизассемблировать данные, он сделает это, ничего не заметив.
  • РАЗМЕЩЕНИЕ ДАННЫХ В ПАМЯТИ
    Команда Enter используется для размещения данных в памяти. Она имеет два режима: Display/Modify и Replace. Отличия между ними в расположении помещаемых данных - в самой команде Enter или после приглашения.
    Если вы ввели E <адрес>, вы будете находиться в режиме Display/Modify. debug.exe предложит вам изменить значение байта, отображая его текущее значение. Вы можете ввести один или два шестнадцатеричных символа. Если вы нажмете пробел, debug.exe не будет изменять текущий байт, а перейдет к следующему. Если вы зашли далеко, нажатие минуса "-" возвратит на один байт назад.
    Код
          -E 100 <Enter>
          15A3:0100   30.41   C0.42   B7.43   10.     B5.45 
          15A3:0105   05.46   B1.40 10.- 
          15A3:0106   40.47   10.
    В нашем примере мы ввели E 100. Debug.exe ответил адресом и значением байта по этому адресу (30). Мы ввели 41, и debug.exe автоматически перешел к следующему байту данных (C0). Опять, мы вводим 42, и debug.exe переходит к следующему байту (B7). Мы изменили его на 43. По адресу 103 байт 10 нас вполне удовлетворяет, поэтому мы нажали пробел. debug.exe не изменил его значение и перешел к следующему байту.
    После ввода 40 в позиции 106 мы обнаружили, что ввели неправильное значение. Нажимаем минус, и debug.exe возвращается на одну позицию назад, отображая адрес и содержимое. Обратите внимание, что оно отличается от первоначального (B5) и имеет значение, которое мы ввели (40). Мы вводим правильное значение и завершаем работку нажатием ENTER.
    Как вы видите, это утомительная работа, особенно когда вы работаете с большими объемами данных или с ASCII-текстом - вы должны знать шестнадцатеричное значение каждого символа. В этом случае можно воспользоваться режимом Replase.
    Режим Display/Modify предназначен для изменения значения небольшого количества байт по различным смещениям. Replase предназначен для изменения любого количества подряд идущих байт.
    Данные можно вводить как в символьном так и в шестнадцатеричном формате, и все байты можно ввести за один раз, не ожидая приглашения отладчика. Если вы хотите разместить строку 'Wind0yZ must Die', заканчивающуюся на 0, по адресу 100, то вы должны ввести
    Код
    : E 100 'Wind0yZ must Die' 0
    Люди!!! То что вы сейчас сделали, подобно самоубийству!!! вы только что отредактировали код свою собственной программы. Причем самым извращенным образом!!!
    Если теперь вы введет команду U, то увидите ЭТО:
    Код
          15A3:0100 57     PUSH DI 
          15A3:0101 69     DB 69 
          15A3:0102 6E     DB 6E 
          15A3:0103 64     DB 64 
          15A3:0104 30795A XOR [BX+DI+5A],BH 
          15A3:0107 206D75 AND [DI+75],CH 
          15A3:010A 7374   JNB 0180 
          15A3:010C 204469 AND [SI+69],AL
    А если вам не дай Бог взбредет в голову ввести команду G, то... Ну, в общем сами увидите. Как и в команде Search, данные в символьном и HEX формате могут чередоваться в любом порядке. Это наиболее удобный способ размещения больших объемов данных в память.
  • РАЗМЕЩЕНИЕ ДАННЫХ ОДИНАКОВОГО ЗНАЧЕНИЯ
    Команда Fill удобна для размещения данных одинакового значения. Она отличается от команды Enter тем, что завершает свою работу только тогда, когда будет полностью заполнен заданный участок памяти. Как и Enter, она работает и с символьными данными, и с шестнадцатеричными значениями. В отличие от Enter, с помощью этой команды можно заполнять большой объем памяти за один раз, без определения значения каждого символа.
    Например, чтобы очистить 32К (8000h) памяти, вы всего лишь должны ввести команду:
    Код
    -F 0 L 8000 0 <Enter>
    В итоге все байты памяти начиная с DS:0000 и до DS:8000 будут обнулены. Если бы вместо нуля мы ввели '1234' , то память была бы заполнена повторяющейся последовательностью '123412341234', и так далее. Обычно, для задания небольших объемов данных лучше использовать команду Enter, потому что ошибка в длине при вызове команды Fill может наделать много бед. Команда Enter изменяет значения только тех байт, которые вы непосредственно укажете, что позволяет минимизировать вероятность ошибки.
  • ПЕРЕМЕЩЕНИЕ ДАННЫХ
    Команда MOVE перемещает данные "внутри компьютера". Она берет данные, расположенные по одному адресу, и копирует их "в другой" адрес.
    Если вы хотите выполнить эту команду во время трассировки, это может нарушить ход ее выполнения и получится очень здорово - данные и инструкции, расположенные после "вставки", будут теперь расположены в совсем другом месте...
    Команда MOVE может быть использована для сохранения части программы в свободной памяти, пока вы будете вносить в нее изменения. Тогда программу можно будет восстановить в любой момент...
    Вы можете вносить изменения "в BIOS" не утруждая себя программированием ROM:
    Код
     - M 100 L 200 ES:100
    Мы копируем данные с адреса DS:0100 до DS:02FF (длина - 200) в область памяти, которая начинается с ES:100. Позднее мы можем их восстановить. Нужно только ввести:
    Код
    - M ES:100 L 200 100
    что скопирует данные туда, где они должны быть. Если мы не изменяли данные в памяти по адресу ES:0100, то эта команда восстановит первоначальное состояние памяти DS:0100 - DS:02FF.
  • АССЕМБЛИРОВАНИЕ

    А вот теперь начинается самое интересное!
    Команда ASSEMBLE запрашивает мнемоники (то бишь команды микроассемблера) и преобразует их в машинный код.
    Есть некоторые операции, которая она не может проделать (в отличие от MASM/TASM/NASM): ссылка на метки, использование макроса или чего-нибудь еще, что не может СРАЗУ транслироваться в машинный код.
    Обращение к данным должно происходить по их физическому адресу в памяти, сегментные регистры, если они отличаются от установленного значения, должны быть определены, и при использовании команды RET должен быть указан тип возврата (NEAR или FAR).
    А еще, если инструкция обращается к данным, а не к регистрам (например, MOV [278], 5), то нужно указывать их длину - Byte ptr или Word ptr. Чтобы указать debug.exe различие между пересылкой 1234h в AX и пересылкой слова по адресу 1234 в регистр AX, используют квадратные скобки - последнее будет иметь вид MOV AX, [1234].
    Всяческие разновидности инструкции JMP автоматически ассемблируются в Short, Near или Far переходы.
    А теперь мы напишем еще одно "окошко" . Только оно будет не очень красивое . Размеры его будут максимально возможными, а атрибут - стандартный досовский. Работать эта программа будет по образу и подобию команды CLS (очистка экрана).
    Код
         -A 100
         15A3:0100 mov ax,600
         15A3:0103 mov cx,0
         15A3:0106 mov dx,184f
         15A3:0109 mov bh,07
         15A3:010B int 10
         15A3:010D int 20
         15A3:010F
    Мы использовали прерывание BIOS 10h, которое предназначено для работы с экраном. Мы обращаемся к BIOS с AX=600, BH=7, CX=0, and DX=184Fh. Сначала необходимо установить регистры, что мы и сделали, введя первые четыре инструкции. Команда по адресу 15A3:010B - команда обращения к BIOS. INT 20 (по адресу 010D) служит для безопасности. Нам эта команда практически не нужна, но когда она есть, программа остановится автоматически. Без INT 20, и если мы сами не остановим программу, DEBUG продолжит выполнение программы (от 010F и дальше). А так как после 010D начинается неопределенная область, то скорее всего система зависнет. Теперь поможет только ctrl-alt-del (может быть) или же выключение и включение питания. Будьте осторожны и дважды проверяйте, прежде чем что-нибудь делать. А еще лучше - трижды...

    Теперь мы должны запустить программу. Чтобы это сделать, введите команду G и нажмите Enter. Если вы правильно ввели свою программу, экран должен очиститься и должно появиться сообщение "Program terminated normally". Более подробно команда Go будет рассмотрена ниже.

    Опять же, я не могу выразить всей важности правильного введения инструкций при использовании Assemble. Особенно нужно быть осторожным с инструкциями типа JMP и CALL. Они изменяют ход выполнения программы, поэтому может случиться так, что выполнение начнется с середины какой-нибудь инструкции, что приведет к крайне нежелательным результатам.
  • NAME (ИМЯ)
    Команда NAME служит только для одной цели - определить имя файла, который debug.exe должен загрузить или сохранить. Она не изменяет память и не выполняет программу, она только формирует "блок контроля" для файла, с которым будет работать debug.exe. Если вы хотите загрузить программу, то можете указать это в этой же строчке параметры, как при работе с ДОС. Единственное отличие - должно быть задано расширение.

    Расширений по умолчанию не существует. DEBUG загрузит или запишет на диск любой файл, если указано его полное имя.
    Код
    -n format.com c:/s
    Мы приготовили debug.exe к загрузке программы FORMAT.COM с заданным ключом. Когда мы введем команду Load (см. ниже), debug.exe загрузит программу format.com с параметрами c:/s.
  • LOAD (загрузить)
    Команда LOAD имеет два формата. Первый загружает программу, которая была определена командой NAME, устанавливает все регистры, готовит все необходимое для исполнения. Все заданные параметры программы будут помещены в PSP, и программа "приготовится" к выполнению.

    Если файл в формате HEX, он должен содержать правильные шестнадцатеричные символы, которые определяют размер памяти. Загруженные файлы выполняются с адреса CS:0100 или с адреса, указанного в команде. Для файлов COM, HEX and EXE регистры содержат адрес первой инструкции программы. Для других типов файлов регистры неопределены. Сегментные регистры имеют значение, указанное в PSP (100h байт перед кодом программы), в BX и CX содержится размер файла. Остальные регистры неопределены.
    Код
         -n format.com
         -l
    Эта последовательность команд загрузит format.com в память, поместит в IP точку входа - 0100, а CX будет содержать HEX-размер файла. Программа теперь готова к работе

    Другой формат команды LOAD не использует команду NAME. Он предназначен для чтения секторов с диска (гибкого или жесткого) в память.
    За один раз можно прочитать 80h (128d) секторов. При использовании этой команды вы должны указать начальный адрес, диск (0=А, 1=В и т. д.), начальный сектор и количество читаемых секторов.
    Например
    Код
    -l 100 0 10 20
    указывает debug.exe загрузить в память с DS:0100 20h секторов с диска А начиная с сектора 10h.
    Таким образом debug.exe можно иногда использовать для восстановления части информации на поврежденном секторе. Но это уже извращение!
  • WRITE(записать)
    Команда WRITE очень похожа на команду LOAD. Обе имеют два режима работы, и обе могут работать как с файлами, так и с физическими секторами. Как вы наверное уже поняли, WRITE производит запись на диск. Поскольку все параметры такие же, как и в LOADе, мы не будем их опять описывать.

    Отметим только одну вещь - при использовании этой команды размер записываемых данных определен в BX и CX, где BX содержит старшую часть размера файла. Начальный адрес должен быть определен по умолчанию - CS:0100. Файлы с расширением .EXE или .HEX не могут быть записаны (появится сообщение об ошибке). Если вы хотите изменить .EXE или .HEX файл, просто переименуйте его, загрузите, сделайте необходимые изменения, сохраните его и дайте ему прежнее имя.
  • ЧТЕНИЕ ДАННЫХ ИЗ ПОРТА
    Команда INPUT предназначена для чтения данных из любого I/O порта PC. Адрес порта может быть как однобайтовым, так и двухбайтовым. Debug.exe произведет чтение из порта и отобразит на экране его содержимое.
    Код
     -i 3fd 7D
    Этой командой мы прочитали данные из "входного" порта "первого асинхронного адаптера". Результат, который вы получите, может отличаться от приведенного - все зависит от текущего состояния порта. У меня в момент чтения регистр порта имел значение 7Dh.
    Естественно, чтение из разных портов может привести к различным результатам.
  • ЗАПИСЬ ДАННЫХ В ПОРТ
    Вы можете использовать команду OUTPUT для того, чтобы послать один байт или последовательность данных в порт. Помните, что запись в некоторые порты может привести к непредсказуемым последствиям. Будьте осторожны при работе с этой командой!
    Код
    -o 3fc 1
    Порт 3FCh - это "регистр контроля модема" для "первого асинхронного порта". Запись в него 01h устанавливает бит DTR. 00h сбрасывает все биты. Если у вас есть модем, который отображает состояния этих бит, вы сможете увидеть вспышку лампочки, когда будете устанавливать и сбрасывать этот бит.
  • GO (выполнить, запустить)
    Команда GO начинает выполнение программы. Она позволяет запускать программу с любой точки и останавливать ее в любой из десяти брекпоинтов программы. Если брекпоинты не установлены (или не выполнены), выполнение программы будет продолжаться до конца, после чего будет выведено сообщение "Program terminated normally".
    Если выполнение программы дошло до брекпоинта, программа будет остановлена, отобразится содержимое регистров и появится обычное приглашение debug.exe. Теперь можно вводить любые команды debug.exe, включая и команду GO для продолжения выполнения программы.

    Команда GO не может быть прервана нажатием Cntl-break. Это одна из тех немногих команд, которые не могут быть прерваны во время исполнения.
    Код
    -g =100
    Команда GО без брекпоинтов начинает выполнение программы с адреса, указанного в параметре.

    Кстати, перед адресом должен стоять знак "равно" - без этого знака адрес воспринимается как брекпоинт...

    Если не указан стартовый адрес, выполнение проограммы начинается с CS:IP.

    Что еще тут сказать? Пример если только привести...
    Код
     -g 176 47d 537 647
    Данной командой мы запускаем программу и устанавливаем брекпоинты по адресам CS:176, CS:47D, CS:537 и CS:647.
  • Несколько слов о бряках теперь...
    Работа проги останавливается непосредственно ПЕРЕД брекпоинтами. Установка их на текущую инструкцию приведет к тому, что программа не будет выполняться. Debug.exe сначала устанавливает брекпоинт, и только потом пытается выполнить программу. Бряки (разновидность бяк) используют INT 3 для остановки выполнения. Debug.exe вызывает прерывание 3 для остановки программы и отображает содержание регистров.
    Брекпоинты не сохраняются между двумя вызовами команды GO. Все брекпоинты вы должны указывать при КАЖДОМ обращении к этой замечательной команде
  • ТРАССИРОВКА
    Команда TRACE имеет сходство с командой GO. Различие между ними заключается в том, что GO выполняет целый блок кода за один раз, а TRACE выполняет инструкции по одной, каждый раз отображая содержимое регистров.

    Как и в GO, выполнение можно начинать с любой точки. Перед стартовым адресом должен стоять знак "равно". При вызове команды можно указать количество инструкций, которые нужно выполнить.
    Код
    -t =100 5
    Эта команда начнет работу с адреса CS:100 и выполнит пять инструкций. Без указания адреса, выполнение начнется с CS:IP. Команда Т без параметров выполнит только одну инструкцию. При использовании TRACE желательно обходить обращения к DOS и другие прерывания. DOS нельзя трассировать - это может привести к плохим последствиям. Можно трассировать программу до инструкции прерывания, затем командой GO выполнить прерывание и продолжать трассировку дальше.
  • ПЛЮС-МИНУС
    С помощью команды с завернутым названием HEXARITHMETIC можно складывать (+) и вычитать (-) шестнадцатеричные числа. Она имеет два параметра: два числа, которые нужно сложить или вычесть. Числа должны иметь длину не более четырех шестнадцатеричных цифр. Сложение и вычитание беззнаковое, не учитывается переполнение старшего разряда.
    Код
         -h 5 6
         000B FFFF
    
         -h 5678 1234
         68AC 4444
    В первом примере мы хотели сложили 0005 и 0006. Их сумма - 000B, а разность -1. Однако, т.к. числа беззнаковые, мы получили FFFF.

    Во втором примере сумма 5678 и 1234 - 68AC, а разность - 4444
Фрагменты статей Рустэма Галеева aka Roustem
Win32 в машинных кодах
взято здесь
Инструменты
Чтобы вводить двоичные значения в компьютер, необходим шестнадцатеричный редактор. Поскольку мы решили обходиться стандартными средствами, имеющимися в любой типичной поставке Windows, используем в качестве шестнадцатеричного редактора старый досовский отладчик debug. Рассмотрим лишь те возможности этого отладчика, которые нам понадобятся в работе.

Сначала имеет смысл создать отдельную папку для проводимых экспериментов, например, \exp. Теперь запустим командную строку DOS, перейдем в созданный каталог (cd \exp) и наберем: debug. Появляется черточка - приглашение отладчка; можно набирать команды. Сразу о том, как завершить работу debug: для этого служит команда q (quit).

Debug позволяет создавать и записывать на диск файлы, но у этого процесса есть некоторые особенности. Дело в том, что создаваемые файлы будут в старом досовском формате com. Для нас это означает, что при записи на диск отладчик использует данные, начиная со смещения 100h кодового сегмента (адрес которого содержится в регистре CS), это надо учитывать. Если наши данные будут начинаться со смещения 0, первые 256 (100h) байтов окажутся утерянными (для содержимого регистров CS и DS по умолчанию). Либо надо вручную изменить (увеличить на 10h) значение регистра DS.

Попробуем создать простейший файл. Запускаем debug. Для записи служит команда w (write); однако вначале должно быть определено имя файла с помощью команды n (name). В принципе, имя может быть любым досовским именем (в коротком формате 8.3), но расширение не может быть exe или hex. Лучше использовать расширение bin, а потом переименовать файл. Набираем:
Код
n first.bin
Теперь необходимо указать размер создаваемого файла. Это значение должно быть в регистрах BX:CX, причем младшее слово содержится в CX, старшее слово - в BX (отладчик debug 16-разрядный, поэтому он не работает с 32-разрядными регистрами и смещениями). Для начала запишем лишь 1 байт; введем с помощью команды r (register) 1 в регистр CX (в BX по умолчанию содержится 0):
Код
r cx
1
Таким способом можно изменять значения любых регистров. Собственно запись осуществляется командой w. Смотрим - в нашем каталоге должен появиться файл 'first.bin' размером в 1 байт.
Перейдем к формированию наших данных. Одна из полезных команд - f (fill), она позволяет заполнить участок памяти указанными данными. После f первым параметром идет смещение (начальный адрес) заполняемого блока, затем либо параметр l (length) и число, указывающее на длину заполняемого участка в байтах, либо смещение его конца. После этого - собственно данные, которыми будет заполняться данный участок. Причем данные могут быть как в виде 16-ричных чисел, так и в виде заключенных в апострофы или кавычки строк, причем их можно чередовать. Например, заполним первые 256 (100h) байт строкой "This is the filling string":
Код
f 0 l 100 'This is the filling string'
Чтобы просмотреть содержимое памяти, служит команда d (dump). Как и в случае с командой f, первый параметр указывает смещение начала отображения данных (дампа), а за ним - либо l с указанием размера дампа, либо конечное смещение. Используем для разнообразия второй вариант (учтите, все используемые числа в debug - 16-ричные):
Код
d 0 ff
Как видим, указанный участок заполнен повторяющейся строкой, которую мы указали в качестве параметра команды f. Разумеется, это лишь пример, а в реальности мы будем эту команду использовать для очистки (заполнения нулями) блоков памяти. Например, очистим первый килобайт (400h байт):
Код
f 0 400 0
Если в команде d указать лишь один параметр, она по умолчанию отображает 80h байт, начиная с данного смещения. А если не указать и его, то отображаются очередные 80h байт с того места, на котором остановились в прошлый раз. Поэтому мы можем набрать:
Код
d 0
Посмотрев первые 80h байт дампа, набираем d и смотрим следующую порцию и т.д.
Теперь проделаем эксперимент, демонстрирующий особенность сохранения файлов в debug. Создадим файл, первые 100h байт которого заполнены символами '0', вторые 100h байт - символами '1' и т.д. до, скажем, '9'. Дадим файлу имя 'first.txt' (или любое другое с расширением .txt), а размер его будет a00h (2,5 Кб).
Код
n first.txt
r cx
a00
f 0 l 100 30
f 100 l 100 31
и т.д. до f 900 l 100 39. 16-ричные числа 30, 31, ... , 39 являются ASCII-кодами цифр 0-9. После этого набираем w и смотрим, что получилось.
Открываем 'first.txt' в Блокноте. Но что это? Файл начинается с единиц, а в конце какой-то мусор? Смотрим в debug'е: d 0 ff - все нормально, заполнено цифрами 0 (30h). Вот это и есть та особенность отладчика, о которой мы говорили в начале. В файл записываются данные начиная со смещения 100h относительно кодового сегмента.
Исправить эту ситуацию можно попытаться двумя способами. Рассмотрим еще одну команду отладчика: m (move). Она позволяет копировать данные из одной области памяти в другую. Первый параметр, как и ранее, является смещением начала участка памяти, который необходимо скопировать, второй - либо смещением конца копируемого участка, либо (при наличии буквы l) его длиной, третий параметр - смещение места назначения, куда надо скопировать данные. С помощью этой команды мы можем "передвинуть" весь наш блок данных так, чтобы он начинался со смещения 100h:
Код
m 0 l a00 100
Теперь снова попробуем записать эти данные в тот же файл. Открываем в Блокноте - то что надо! Начинается нулями, заканчивается девятками, ничего лишнего, только то, что мы сами вводили.

Второй способ - изменить значение регистра сегмента данных DS таким образом, чтобы он указывал на область со смещением 100h относительно начала кодового сегмента. Т.е. надо просто добавить к старому значению DS 10h. Допустим, в DS было значение 2020. Изменим его на 2030:
Код
r ds
2030
Теперь затрем старые данные, скажем, числом ff:
Код
f 0 l a00 ff
Запишем это в старый файл командой w и убедимся, что файл изменился. И повторим старую операцию:
Код
f 0 l 100 30
f 100 l 100 31
...
f 900 l 100 39
w
Результат аналогичный ранее сделанному.
Команда e (enter) позволяет вводить данные по конкретным адресам. Первый параметр указывает начальный адрес, остальные рассматриваются как данные для ввода. Причем здесь тоже можно использовать как 16-ричные числа, так и символьные строки, чередуя их между собой произвольным образом.
В связи с данной командой рассмотрим особенность процессоров IA-32, о которой говорилось в прошлой статье. Речь идет об "обратном" представлении чисел в памяти; хотя по внимательном рассмотрении этого вопроса представление чисел в процессорах IA-32 как раз является естественным ("нормальным"), а "обратным" оказывается наша традиционная запись. Попробуем разобраться.
Мы читаем и записываем слева направо. Если записать порядковые номера, они будут увеличиваться тоже слева направо. Естественно таким же образом нумеровать объекты, скажем, байты памяти: 1, 2, 3, 4 и т.д. Значения возрастают слева направо. Теперь посмотрите на числа, у которых увеличиваются разряды: 1, 10, 100, 1000. Каждый новый разряд мы добавляем слева, т.е. возрастание числа получается справа налево - порядок, противоположный традиционному письму. Если сохранять в памяти текст, т.е. строку символов, при добавлении новых символов они будут помещаться "правее", т.е. по возрастающим адресам памяти (поскольку мы нумеруем их слева направо). А как быть, если увеличивается значение числа и оно перестает помещаться на старом месте? Скажем, вместо байта требуется уже слово (два байта)? Новый байт можно добавить "слева" (с меньшим адресом) или "справа" (с большим адресом). Поскольку адресом многобайтной конструкции по соглашению считают самый младший адрес, он может указывать либо на байт, в котором хранятся старшие разряды числа, либо на байт, в котором хранятся младшие разряды. Первый способ называется "big-endian", второй - "little-endian". Так вот, в процессорах IA-32 используется "little-endian", т.е. старшие разряды добавляются "справа" (по старшим адресам памяти) - порядок, обратный нашей записи чисел. Говорят, в свое время Фибоначчи, заимствуя цифры у арабов, не учел особенностей их письма: арабы пишут справа налево, в отличие от нас. И так же располагались разряды их цифр. Фибоначчи использовал тот же порядок, хотя европейцы писали в обратном направлении - вот где корень всех наших бед .
Таким образом, если мы хотим разместить по адресу 10h число 12h, мы набираем:
Код
e 10 12
Если же мы хотим разместить по этому же адресу число 1234h, два байта, его составляющих, нам придется вводить следующим образом:
Код
e 10 34 12
А если по тому же адресу нужно записать число 12345678h, ввод будет таким:
Код
e 10 78 56 34 12
Только в этом случае в результате исполнения инструкции копирования данных из памяти (по адресу 10h) в регистр EAX, которую мы рассматривали в прошлой статье, в регистре EAX окажется нужное нам значение 12345678h.
Как вы уже, очевидно, заметили, в 16-разрядной системе используется сегментная модель памяти. Это создает дополнительные проблемы; в частности, команды заполнения (f) и перемещения (m) не работают через границы сегментов. Поэтому, хотя debug в принципе позволяет сохранять файлы размером более одного 16-разрядного сегмента (64 Кб), при составлении таких файлов у нас могут возникнуть проблемы. Их можно решить другим путем - собирая в debug отдельные "модули", не превышающие 64 Кб, и соединяя их с помощью команды DOS copy.
Для доказательства такой возможности соберем простой текстовый файл размером в 1 Мб. Собирать будем из 16 модулей в 64 Кб, сохраненных средствами debug; каждый модуль будет заполнен единственным символом - 16-ричной цифрой, значение которой равно номеру модуля (для контроля).
Сначала настроим регистр DS (если он не был настроен ранее), увеличив его значение на 10h. В регистр CX должно быть значение 0, в BX - 1 (это соответствует размеру файла 10000h байт, или ровно 64 Кб):
Код
r ds
<ввести значение на 10h большее прежнего>
r cx
0
r bx
1
Если параметр с буквой l в командах равен 0, длина участка памяти считается равной размеру полного сегмента, т.е. 64 Кб. Будем последовательно заполнять весь сегмент символом очередной 16-ричной цифры (от 0 (30h) до 9 (39h) и далее от A (41h) до F (46h)) и сохранять его под новым именем:
Код
n 0.bin
f 0 l 0 30
w
n 1.bin
f 0 l 0 31
w
. . .
n 15.bin
f 0 l 0 46
w
В нашем каталоге должны появиться 16 файлов с расширением bin и размером 64 Кб каждый. Теперь выходим из debug (q) и набираем в командной строке:
Код
copy /b 0.bin+1.bin+2.bin+3.bin+...+15.bin 16.txt
Естественно, вместо "..." здесь должны быть имена остальных файлов, соединенных знаком "+". Откроем итоговый файл 16.txt в WordPad (Блокнот для этой цели не годится - слишком большой файл) и убедимся, что он заполнен введенными нами символами и что в нем нет ничего лишнего.
Осталось рассмотреть лишь некоторые методы автоматизации нашей работы. Работать с debug, все время вводя данные в интерактивном режиме, может оказаться утомительным - удобнее использовать заранее подготовленные шаблоны, внося в них каждый раз небольшие изменения. Для этого воспользуемся еще одной возможностью ОС - перенаправлением ввода-вывода.

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

debug < batch.txt
Для испытания этого способа повторим тот же алгоритм, который мы использовали при создании файла "first.txt". В Блокноте создаем файл "batch.txt" со следующим содержимым:
Код
n first.txt
r cx
a00
f 0 l 100 30
f 100 l 100 31
. . .
f 900 l 100 39
m 0 l a00 100
w
q
В конце файла надо не забыть поставить q - иначе мы останемся в отладчике. Результаты работы все еще выводятся на экран. Их можно записать в файл (иногда это бывает полезно), использовав второе перенаправление:
Код
debug < batch.txt > batch.lst
Теперь в консольном окне сообщения не выводятся, зато в файле batch.lst оказались записанными введенные нами команды и ответы на них отладчика. Заметим, что таким способом мы не можем использовать команды, требующие анализа ответов отладчика. Например, мы не сможем воспользоваться изменением значения регистра DS, поскольку заранее (в общем случае) неизвестно его значение.
Наконец, рассмотрим еще одну команду - a (assemble). Эта команда позволяет войти в режим ассемблирования, т.е. ввода инструкций на ассемблере, которые debug автоматически преобразует в машинные коды. Однако делает это он в 16-разрядном режиме, что нам совершенно не подходит. Но мы можем воспользоваться в этом режиме директивой db, позволяющей вводить отдельные байты, как в команде e. Это может напомнить путешествие из Петербурга в Москву через Владивосток; однако, удобство этого метода в том, что отладчик будет автоматически подсчитывать смещение следующей вводимой инструкции (в нашем случае - байта), и можно не считать все самим.
Параметром команды a является адрес (смещение), с которого мы начинаем вводить инструкции. Чтобы выйти из режима ассемблирования, необходимо просто нажать 'Enter' еще раз (в тексте пакетного файла в этом месте должна быть пустая строка). Потренируемся в использовании этой команды с использованием инструкций в машинных кодах, которые мы составляли в прошлый раз (впрочем, ничто не мешает составить и новые). Сначала поработаем в интерактивном режиме:
Код
a 100
В ответ слева появится что-то типа 2020:0100. Старшее слово (сегмент) нам неинтересно, а младшее (справа) как раз и является текущим смещением от начала сегмента. Набираем:
Код
db b8 01 00 00 00
После нажатия 'Enter' появляется новое смещение - 105. К старому смещению автоматически прибавилась длина введенных нами данных. Вводим:
Код
; конец инструкции
Смещение осталось тем же. Все содержимое строки после точки с запятой игнорируется. Очень удобно - можно использовать, как метки для соответствующих смещений. Продолжаем:
Код
db b4 01
; конец второй инструкции
db "Some text"
Как видим, длина текстовых строк тоже подсчитывается автоматически и добавляется к смещению. Запишем все в файл, выйдя из режима ассемблирования (для этого просто нажимаем на 'Enter' еще раз):
Код
<Enter>
n second.bin
r cx
10
w
Заметим, что длину введенных данных подсчитать теперь очень просто: отнимаем от конечного смещения (в данном случае 110h) начальное: 110-100=10h.
Но преимущества режима ассемблирования станут очевидными при работе с перенаправлениями. Создадим файл "second.txt" и наберем в нем те же данные (не забыв про пустую строку в соответствующем месте и команду q в конце). В командной строке DOS запишем:
Код
debug < second.txt > second.lst
В данном случае нас особо интересует именно выходной файл - second.lst. Теперь все смещения записаны в файле. Это дает возможность при "первом проходе" (черновом) вводить приблизительные значения (здесь это могло бы быть, например, значение регистра CX). Выходной файл используется затем для получения точных значений смещений и подстановки их в исходный файл с командами для "второго прохода" (чистового).
Завершим знакомство с отладчиком способами загрузки созданных заранее шаблонов. Для загрузки файлов служит команда L (Load). При этом имя файла должно быть уже указано командой n. Файл загружается по смещению 100h. Либо имя файла можно указать в качестве параметра при вызове отладчика:
Код
debug first.txt
Произведя нужные изменения, файл можно сохранить под другим именем. При этом, если работа с debug ведется со смещениями, меньшими 100h, новое имя файла нужно вводить заранее, т.к. debug записывает имя в эту область, и данные могут оказаться испорченными.
Для примера рассмотрим, как можно загрузить в качестве шаблона созданный ранее файл "first.txt" и сохранить его после сделанных изменений. Сначала создаем "автоматизирующий" файл с командами ("third.txt"):
Код
n third.bin
m 100 l a00 0
f 100 l 100 ff
m 0 l a00 100
w
q
Теперь в командной строке набираем:
Код
debug first.txt < third.txt > third.lst
В данном случае отладчик загружается вместе с файлом "first.txt", затем он исполняет команды, содержащиеся в "third.txt" (создавая в процессе работы файл "third.bin"), а отчет записывает в файл "third.lst".
Финальный штрих - полная автоматизация создания исполняемого файла. Для этого создается bat-файл, в котором записываются вызов самого debug, а также другие необходимые действия, например, составление одного большого файла из отдельных модулей с помощью команды copy или переименование файла с расширением bin в файл с расширением exe. Создадим в Блокноте файл "make.bat":
Код
@echo off
debug < second.txt
ren second.bin second.exe
Теперь можно запустить этот файл на исполнение (двойным щелчком по его имени в Проводнике или набрав имя в командной строке). Строка "@echo off" нужна для того, чтобы команды в bat-файле не выводились на экран. Однако, результат работы debug все равно будет отображаться на экране; чтобы его не было, можно использовать второе перенаправление - либо в файл, либо, если файл с результатами работы не нужен, сюда можно записать nul:
Код
debug < second.txt > nul
Результатом работы будет файл second.exe. Кстати, можете попробовать запустить его - ничего страшного не произойдет, система просто сообщит, что это не настоящий исполняемый файл Windows. Отметим, что это простейший случай; на самом деле в bat-файле может быть записано множество вызовов debug с различными заранее подготовленными файлами для создания сразу нескольких модулей и последующего их объединения в один результирующий.
Каков же итог? Любой файл - будь то картинка, векторная или трехмерная графика, музыка, видео или исполняемый - это всего лишь сохраненный набор двоичных чисел. А итог таков, что мы умеем теперь создавать файлы практически любого размера и с любым содержанием. Единственное, что при этом надо - это изучить формат соответствующего типа файла. Этим мы и займемся в следующей статье применительно к исполняемым файлам Windows.
12
Ушел с форума
Автор FAQ
15840 / 7422 / 994
Регистрация: 11.11.2010
Сообщений: 13,385
30.05.2013, 10:22  [ТС] 6
Исполняемые файлы Windows
Как сделать, чтобы программа заработала? Работа приложения начинается с того, что операционная система создает процесс. Это не просто загруженная в память программа пользователя; процесс предполагает создание множества внутренних системных структур для обеспечения работы программы и предоставления ей различных ресурсов, таких как память, процессорное время, доступ к установленному в системе оборудованию и так далее.
Важнейшим ресурсом являетcя виртуальная память. Каждый процесс получает в свое распоряжение собственное виртуальное адресное пространство памяти размером 4 Гб. Это значит, что он может обращаться по любому из адресов памяти от 0 до FFFFFFFFh. Но это значит также и то, что различные процессы могут использовать одни и те же адреса, не мешая друг другу. Система работает с памятью в виде блоков фиксированного размера, называемых страницами (обычно по 4 Кб; на современных процессорах могут быть страницы также по 2 Мб) и использует страничную переадресацию для отображения одних и тех же виртуальных адресов различных процессов в разные области физической памяти. Кроме того, при недостатке физической памяти временно неиспользуемые данные могут сохраняться на диске, освобождая физическую память для других виртуальных адресов (это называется подкачкой).
В адресном пространстве процесса резервируются области для динамически выделяемой памяти ("кучи") и стека (о нем мы подробнее поговорим в следующей статье). Затем образ программы загружается из файла на диск по базовому адресу загрузки. Образ программы состоит из одной или нескольких секций. Для каждой секции выделяется несколько страниц памяти, имеющих одинаковые атрибуты. Например, это могут быть исполняемые страницы, страницы только для чтения или для чтения и записи. Это сделано для уменьшения количества возможных ошибок; например, случайный запуск на исполнение страницы, содержащей не код, а данные, может привести к непредсказуемым результатом. Если же в атрибутах страницы не указана возможность исполнения, это приведет к сообщению об ошибке. Точно так же атрибут "только для чтения" позволяет перехватить попытку случайной или преднамеренной записи на страницу, содержание которой не должно изменяться (допустим, если она содержит константы).
Расширение "exe" осталось в наследство от старых DOS-овских исполняемых (executable) файлов. Используемый в настоящее время формат исполняемых файлов Windows называется "Portable Executable" (PE), поскольку один и тот же формат используется для разных платформ. Более того, он построен на основе шаблонов, являющихся общими и для объектных файлов формата COFF (используемых в том числе в мире Unix), а также построенных на их основе библиотечных файлов и файлов импорта (.lib). Формат PE в системе Win32 является универсальным: его используют не только исполняемые файлы (exe), но и динамические библиотеки (dll) и их особые разновидности -элементы ActiveX (ocx) и системные драйверы (sys и drv).
Как и старый формат exe для DOS, PE-файл состоит из заголовка и собственно образа исполняемой программы. Образ программы, как уже отмечалось, может быть составлен из одной или нескольких секций. Заголовок же можно условно разделить на "старый" (DOS-заголовок и DOS-стаб) и "новый" (все остальное)
"Старый" заголовок, в свою очередь, составлен из слегка модифицированного DOS-заголовка и так называемой программы-заглушки (DOS-стаб), и фактически представляет собой небольшую программу DOS, выводящую простое текстовое сообщение наподобие "This program cannot be run in DOS mode". Это сделано для того, чтобы при ошибочной попытке запуска программы Windows под DOS она могла сообщить об ошибке. Модификация заголовка DOS заключается в том, что по смещению 3Ch от начала файла расположено 32-разрядное смещение PE-заголовка.
"Новый" заголовок составлен из собственно PE-заголовка и таблицы секций, которая фактически является картой отображения записанных в файле секций образа программы в память. В PE-заголовке выделяют также несколько составных частей, но для нашего рассмотрения они несущественны. Отметим лишь каталог смещений-размеров, который указывает на расположение и размеры специальных служебных таблиц. Для размещения последних могут быть выделены отдельные секции в образе программы, но это не является обязательным; в принципе, для всей программы можно использовать одну единственную секцию, разместив в ней и данные, и код, и все необходимые вспомогательные структуры.
Теперь рассмотрим все подробнее. Поскольку попытка запуска создаваемых нами программ под DOS маловероятна, можно без особых проблем обойтись без программы-заглушки DOS. PE-заголовок в этом случае будет следовать сразу за старым заголовком DOS, а именно - непосредственно после 4-байтного поля со смещением 3Ch, то есть по смещению 40h (само поле 3Ch будет содержать в данном случае это же значение). Единственное, что нужно еще оставить в старом заголовке - это сигнатуру в виде 2 ASCII-символов 'MZ' в начале файла (байты 4Dh 5Ah). Остальные поля могут содержать нули - загрузчик Windows их не использует.
Поля PE-заголовка приведены в таблице 1. Смещения указаны относительно начала заголовка, а жирным шрифтом выделены те поля, при неверных значениях которых Windows откажется загружать программу. Остальные поля либо содержат необязательные данные (например, указатель на размещение и размер отладочных данных), либо для них предусмотрены значения по умолчанию (как для размеров кучи и стека), либо используются лишь для определенных видов файлов (например, флаги dll или контрольная сумма).
Таблица 1. PE-заголовок
СмещениеРазмер, байтПолеТипичное значение
04Сигнатура 'PE'50h 45h 00 00
42Тип процессора14Ch
62Число секций в образе программы-
84Время/дата создания файла-
0Ch4Указатель на таблицу символов0
10h 4 Количество отладочных символов 0
14h 2 Размер дополнительного заголовка E0h
16h 2 Тип файла 10Fh
18h 2 "Магическое" значение 10Bh
1Ah 1 Старшая версия компоновщика -
1Bh 1 Младшая версия компоновщика -
1Ch 4 Размер кода -
20h 4 Размер инициализированных данных -
24h 4 Размер неинициализированных данных -
28h 4 Смещение точки входа -
2Ch 4 Смещение секции кода в памяти -
30h 4 Смещение секции данных в памяти -
34h 4 Адрес загрузки образа в память 400000h
38h 4 Выравнивание секций в памяти 1000h
3Ch 4 Выравнивание в файле 200h
40h 2 Старшая версия Windows 4
42h 2 Младшая версия Windows 0
44h 2 Старшая версия образа -
46h 2 Младшая версия образа -
48h 2 Старшая версия подсистемы 4
4Ah 2 Младшая версия подсистемы 0
4Ch 4 Зарезервировано 0
50h 4 Размер загруженного файла в памяти -
54h 4 Размер всех заголовков в файле -
58h 4 Контрольная сумма 0
5Ch 2 Подсистема 2 или 3
5Eh 2 Флаги dll 0
60h 4 Зарезервированный размер стека 100000h
64h 4 Выделенный размер стека 1000h
68h 4 Зарезервированный размер кучи 100000h
6Ch 4 Выделенный размер кучи 1000h
70h4 Устарело 0
74h4Число элементов в каталоге смещений10h
Далее в PE-заголовке следует каталог размещения вспомогательных таблиц: первые 4 байта для каждого элемента являются смещением начала соответствующих данных относительно базового адреса загрузки, следующие 4 байта - размером этих данных. Хотя число элементов в каталоге указывается в поле PE-заголовка, Windows 9* не допускает значения, меньшего 10h. Структура каталога фиксирована; указатели на соответствующие данные должны следовать в следующем порядке:
  1. таблица экспорта;
  2. таблица импорта;
  3. таблица ресурсов;
  4. таблица исключений;
  5. таблица сертификатов;
  6. таблица настроек;
  7. отладочные данные;
  8. специфичные для архитектуры данные;
  9. глобальный указатель;
  10. таблица локального хранилища потоков (TLS);
  11. таблица конфигурирования загрузки;
  12. таблица связанного импорта;
  13. таблица импортируемых адресов (IAT);
  14. дескриптор отложенного импорта;
  15. зарезервировано;
  16. зарезервировано.
Это не значит, что все перечисленные данные должны присутствовать. Если те или иные данные отсутствуют, соответствующие поля каталога содержат нули. Мы будем рассматривать эти структуры по мере того, как начнем с ними работать.
Таблица секций следует непосредственно после PE-заголовка (после каталога смещений). Каждый вход таблицы имееет следующий формат (см. табл. 2).
Таблица 2. Строка таблицы секций
Смещение Размер, байт Поле
0 8 Произвольное имя секции
8 4 Размер секции в памяти
0Ch 4 Смещение секции в памяти относительно адреса загрузки
10h 4 Размер данных секции в файле
14h 4 Смещение начала данных секции в файле
18h 12 Используется лишь в объектных файлах
24h 4 Флаги секции
Таблица секций имеет столько входов, сколько секций в образе программы. Расположение секций в файле и в виртуальной памяти созданного процесса может не совпадать. Данные различных секций как в файле, так и в памяти располагаются не вплотную друг к другу - они должны быть соответствующим образом выровнены. Например, если код занимает всего 2 байта, следующая за ним секция (допустим, данных) располагается не по смещению +2 байта, а на границе следующей страницы, т.е. как минимум через 4 Кб, если это образ в памяти, и минимум через 512 байт для образа в файле. Значения для выравнивания в файле и в памяти указаны в PE-заголовке, причем они обязательны.

Секция может содержать т.н. неинициализированные данные. Фактически, это просто резервирование определенных адресов памяти под будущие переменные. Для таких данных место в файле не отводится; память резервируется лишь при загрузке на исполнение. Если вся секция содержит лишь неинициализированные данные, поля размера данных секции в файле и смещения начала данных секции в файле равны нулю. В любом случае, когда размер секции в файле меньше указанного размера секции в памяти, остаток заполняется до нужного размера нулями.

Поле флагов секции - то самое, где задаются атрибуты страниц памяти, отводимых под секцию. Возможно использование до 32 флагов (по одному на каждый бит 4-байтного значения), но часть из них зарезервирована, другая часть используется лишь в объектных файлах. Биты нумеруются от младшего к старшему, начиная от 0 (самый младший бит - 0, самый старший - 31). Наиболее употребительные для исполняемых файлов следующие:
бит 5 секция кода;
бит 6 инициализированные данные;
бит 7 неинициализированные данные;
бит 28 секция может быть общей (разделяемой - shared);
бит 29 разрешено исполнение;
бит 30 разрешено чтение;
бит 31 разрешена запись.
Например, в секции кода с разрешениями на чтение и исполнение установлены следующие флаги:
Код
01100000 00000000 00000000 00100000 (60 00 00 20 h)
Секция с инициализированными данными с разрешениями на чтение и запись:
Код
11000000 00000000 00000000 01000000 (C0 00 00 40 h)
Та же секция, но с разрешением только для чтения:
Код
01000000 00000000 00000000 01000000 (40 00 00 40 h)
Перейдем, наконец, к практике и составим шаблон заголовка PE-файла, имеющего 3 секции с минимальными размерами. Тогда в памяти каждая будет занимать 1000h (1 страница - отвести меньше памяти невозможно), а в файле - 200h байт (1 сектор диска). Такими же будут и значения выравнивания. Первой пусть идет секция кода; назовем ее '.code' (см. рис.) Она будет располагаться по смещению 200h от начала файла, а в памяти - по смещению 1000h от адреса загрузки (первую страницу памяти и первые 200h байтов файла занимает заголовок). Секция кода будет иметь флаги, которые мы вычислили ранее (60000020h)
Секции исполняемого файла
Следующей будет секция с данными только для чтения; назовем ее '.rdata'. Она будет расположена в файле по смещению 400h, а в памяти - по смещению 2000h. Флаги: 40000040h. За ней - секция данных с разрешениями на чтение и запись: '.data', расположение в файле - 600h, в памяти - 3000h; флаги: C0000040h.
Теперь составим командный файл для отладчика debug. Имеет смысл сначала создать специальную папку "Шаблоны". В ней сохраним этот файл для использования в дальнейшем. Открываем Блокнот и набираем:
Код
n Header.bin
r cx
200
f 0 l 200 0
e 0 'MZ'
e 3C 40
e 40 'PE'
e 44 4C 01
Бинарный файл с заголовком будет называться 'Header.bin', его размер - 200h байт. Сначала очищаем "область сборки" - первые 200h байт, затем набираем стандартные сигнатуры. Программы-заглушки у нас не будет - PE-заголовок следует непосредственно за DOS-заголовком; заодно это сэкономит размер заголовка.
А вот дальше пойдут поля PE-заголовка, которые нужно будет настраивать для каждого отдельного exe-файла. Чтобы было удобнее редактировать этот файл в дальнейшем, оставим здесь комментарии - а для этого нам придется изменить способ ввода и перейти в режим ассемблирования.
Код
a 46
; Здесь должно быть число секций (2 байта) *****
db 03 00
<пустая строка>
Режим ассемблирования начинается с команды 'a', за которой следует смещение, по которому нужно вводить данные. В нашем случае, PE-заголовок начинается со смещения 40h от начала файла, поэтому к значениям смещения в таблице 1 нужно добавлять 40h. Близко отстоящие друг от друга поля можно набирать "в один заход"; когда же разрыв большой, можно выйти из режима ассемблирования (оставив для этого пустую строку) и вновь набрать 'a' уже с новым смещением. В "разрыве" при этом останутся нули. Учтите, что комментарии можно оставлять лишь "внутри" режима ассемблирования - вне его отладчик выдаст ошибку.
Имеет смысл также выделить те участки, которые нужно будет в дальнейшем редактировать (как этот случай - число секций может каждый раз быть разным); для этого удобно выделять каким-либо способом строку с комментарием, чтобы она сразу бросалась в глаза. Оставшуюся часть файла для debug приведем, как есть; она не должна вызвать проблем (обратите внимание на пустые строки - их нельзя удалять; и помните про обратный порядок байтов в числах, требующих более 1 байта):
Код
a 54
; Размер дополнительного заголовка
db e0 00
; Тип файла
db 0F 01
; "Магическое" значение
db 0B 01

a 68
; Здесь должно быть смещение точки входа 
; относительно адреса загрузки (4 байта) *****
db 00 10 00 00

a 74
; Начальный адрес загрузки (4 байта) *****
db 00 00 40 00
; Выравнивание секций (4 байта)
db 00 10 00 00 
; Выравнивание в файле (4 байта)
db 00 02 00 00
; Старшая версия Windows (2 байта)
db 04 00

a 88
; Старшая версия подсистемы (2 байта)
db 04 00

a 90
; Здесь должен быть размер загруженного файла 
; в памяти (4 байта) *****
db 00 40 00 00
; Размер всех заголовков в файле (4 байта)
db 00 02 00 00 

a 9C
; Подсистема: 02 - графическая, 03 - консольная (2 байта)
db 02 00 

a A0
; Зарезервированный размер стека (4 байта)
db 00 00 10 00
; Выделенный размер стека (4 байта)
db 00 10 00 00
; Зарезервированный размер кучи (4 байта)
db 00 00 10 00
; Выделенный размер кучи (4 байта)
db 00 10 00 00

a B4
; Число элементов каталога смещений (4 байта)
db 10 00 00 00
;************
; Здесь начинается первый элемент каталога:
; но у нас пока ничего нет - оставляем нули

a 138
; Начало таблицы секций
;
; имя первой секции (8 символов)
db '.code' 0 0 0
; размер секции в памяти (4 байта)
db 00 10 00 00
; смещение секции относительно адреса загрузки (4 байта)
db 00 10 00 00
; размер данных секции в файле (4 байта)
db 00 02 00 00
; смещение начала данных секции в файле (4 байта)
db 00 02 00 00
; Пропускаем 12 байтов
db 0 0 0 0 0 0 0 0 0 0 0 0
; атрибуты первой секции (4 байта):
; код, разрешено исполнение и чтение
db 20 00 00 60
;
; данные второй секции - аналогично:
db '.rdata' 0 0
db 00 10 00 00
db 00 20 00 00
db 00 02 00 00
db 00 04 00 00
db 0 0 0 0 0 0 0 0 0 0 0 0
db 40 00 00 40
;
; данные третьей секции
db '.data' 0 0 0
db 00 10 00 00
db 00 30 00 00
db 00 02 00 00
db 00 06 00 00
db 0 0 0 0 0 0 0 0 0 0 0 0
db 40 00 00 C0

m 0 l 200 100
w
q
Перед записью созданного "образа заголовка" сдвигаем его на 100h байт, чтобы все записалось правильно. Сохраним этот текст в файле "Header.txt".
Теперь у нас есть шаблон, который можно вставлять в начало exe-файла с 3 секциями, размеры которых не превышают 200h байт каждая. Чтобы протестировать его, нужно собрать "настоящий" exe-файл с его использованием. Для этого немного схитрим: вставим две пустые секции (содержащие лишь нули) в качестве секций данных; а в секции кода используем всего 2 байта: EB FE. Это инструкция, передающая управление на себя (как мы узнаем в дальнейшем). Т.е. наша программа просто зацикливается; но пока нам большего и не надо.
В блокноте создадим еще 2 простых файла. Первый - "s1.txt" (содержит наш "код"):
Код
n s1.bin
r cx
200
f 100 l 200 0
e 100 eb fe
w
q
Второй - "s2.txt" (секция в 200h байт, заполненная нулями):
Код
n s2.bin
r cx
200
f 100 l 200 0
w
q
А теперь в том же Блокноте создаем файл "make.bat":
Код
@echo off
debug < header.txt > report.lst
debug < s1.txt >> report.lst
debug < s2.txt >> report.lst
copy /b header.bin+s1.bin+s2.bin+s2.bin nil.exe
Первый вызов debug исполняет команды, записанные в файле header.txt (при этом создается файл header.bin). Отчет выводится в файл report.lst; это необходимо для того, чтобы можно было проверить, не были ли допущены ошибки.
Второй вызов debug исполняет команды в файле s1.txt, создавая файл s1.bin с нашей "секцией кода". Перенаправление с двумя знаками >> означает, что отчет записывается не с начала указанного файла (затирая его содержимое), а добавляется в его конец. Третий вызов debug выполняет s2.txt, создавая пустую секцию в файле s2.bin. Наконец, мы объединяем эти секции в единый файл с расширением exe, причем заметьте - файл s2.bin использован дважды (2 пустые секции).
Теперь полученный файл можно попытаться запустить. Но перед этим неплохо бы еще раз тщательно проверить все исходные файлы - вероятность допущенной ошибки довольно велика. Просмотрите файл report.lst - нет ли сообщений отладчика об ошибках. В частности, типичной ошибкой является случайное использование в командах вместо латинских букв кириллицы (особенно одинаковых - c и с, e и е и т.д.) Если файл создан правильно, ничего не произойдет - сообщения Windows будут лишь при наличии ошибки. Зато нажав Ctl-Alt-Del, вы увидите исполняющуюся задачу 'nil'. Выделите ее и нажмите кнопку "Завершить процесс" - пока мы можем закрыть эту программу только таким способом.
Простейшее приложение
В первой статье мы получили представление о строении машинных инструкций. Во второй статье научились составлять с использованием подручных средств файлы любого уровня сложности. Наконец, в прошлой статье начали создавать исполняемые файлы, которые система "признает" в качестве своих. Теперь мы вплотную стоим перед дверями, открывающими доступ к неисчислимым сокровищам мира Windows.
Это API - Application Programming Interface - интерфейс прикладного программирования, огромная библиотека уже готовых наборов инструкций, входящая в состав самой системы и служащая для выполнения разнообразнейших задач почти на все случаи жизни - имеется в виду, жизни в мире Windows .
Сокровища эти упакованы в виде процедур и функций и размещены в системных модулях - dll. Чтобы получить к ним доступ, необходимо связаться с соответствующими модулями и импортировать из них нужные нам функции. Попробуем сегодня создать элементарное приложение, выводящее простое сообщение с набранным нами текстом и на этом завершающее свою работу. Эту работу осуществляет функция MessageBoxA, находящаяся в модуле User32.dll. Чтобы не пришлось "прибивать" наше приложение из менеджера задач, как в прошлый раз, оно должно также содержать для своего нормального завершения вызов функции ExitProcess из модуля Kernel32.dll.
Обычно для своей работы эти системные функции используют какие-то наши данные, которые мы им должны передать в виде параметров. Например, функции MessageBoxA мы должны предоставить текст, который хотим отобразить. В первой статье мы рассматривали инструкции, позволяющие копировать данные из одного места в другое. Однако, в данном случае мы ничего не знаем о внутреннем "устройстве" вызываемых функций и о том, в каком месте они хранят данные, с которыми работают. Как раз для подобных случаев был изобретен в свое время механизм, получивший название стека.
Стек - это некоторая область в виртуальном адресном пространстве процесса, специально предназначенная для временного хранения данных. Если помните, при создании PE-заголовка мы заполняли поля с размером выделяемого стека. Место, где разместить стек, выбирает система, а соответствующий адрес памяти сохраняет в регистре ESP. Этот регистр специально предназначен для работы со стеком и поэтому не должен использоваться ни для каких других данных.
Доступ же к стеку осуществляется последовательным образом, причем операнд всегда должен иметь размер в 4 байта (для 32-разрядного режима). Для записи очередного числа значение ESP уменьшается на 4 и указывает на "чистую" область размером в 4 байта. В эту область и копируется единственный операнд соответствующей инструкции. При извлечении сохраненного числа из стека копируется 4-байтное значение, адрес которого находится в настоящий момент в ESP, а затем значение ESP увеличивается на 4 и указывает уже на предыдущее сохраненное число. Единственное, за чем необходимо следить - чтобы каждому случаю помещения данных в стек соответствовал ровно один случай извлечения этих данных из стека (это называется сбалансированностью стека).
Рассмотрим инструкции помещения данных в стек (инструкции извлечения данных из стека нам пока не понадобятся, и мы займемся ими в другой раз). На ассемблере группа данных инструкций обозначается мнемоникой PUSH. Сначала инструкция помещения в стек непосредственного значения:
Код
011010 s 0 <байты данных>
Эта инструкция имеет бит знакового расширения s. Если s = 0, то за опкодом следуют 4 байта, которые необходимо скопировать в стек. Если же s = 1, за опкодом следует всего один байт. Но перед тем, как поместить его в стек, производится его т.н. знаковое расширение до 4 байтов: старшие 3 байта заполняются значением старшего бита данного байта. Например, если байт данных был 01000000, он становится 00000000 00000000 00000000 01000000. Если же он был 10000000, то получается 11111111 11111111 11111111 10000000. Получившиеся 4 байта и помещаются в стек. Это позволяет сохранить знак числа, записанного в виде двоичного дополнения, и в то же время сэкономить 3 байта при операциях с небольшими числами (от -128 до +127). Например, команда помещения в стек нулевого значения (PUSH 0) кодируется так:
Код
01101010 00000000 (6Ah 00h)
Следующая инструкция сохраняет в стеке значение общего регистра:
Код
01010 reg
Инструкция сохранения в стеке значения регистра EAX (PUSH EAX) займет всего 1 байт: 01010000 (50h).
Наконец, еще одна инструкция, использующая уже знакомый нам по первой статье байт ModR/M. Этот байт позволяет записывать в стек значения, хранящиеся в памяти. Но в данном случае есть одна особенность использования этого байта. Вспомните, что байт ModR/M предполагает наличие двух операндов (один из которых всегда находится в регистре). Здесь же необходимо указывать лишь один операнд - другой все время один и тот же и задается неявно (адрес в ESP). Поэтому поле REG байта ModR/M, служившее для обозначения регистра, теперь используется в качестве расширения опкода и для данной конкретной инструкции всегда одно и то же (постоянно). А сама инструкция вместе с байтом ModR/M выглядит так:
Код
11111111 Mod 110 R/M
Обратите внимание, что у нас снова появляется альтернативное кодирование - теперь для команды помещения в стек значений регистров (при Mod=11). Например, указанная выше инструкция PUSH EAX может быть закодирована и таким образом:
Код
11111111 11110000 (FFh F0h)
Данные в виде параметров передаются функциям Windows именно через стек. Рассмотрим функцию MessageBoxA, которая принимает 4 параметра. Первым в стек помещается число, указывающее на стиль создаваемого окна сообщения. Это число представляет собой битовую структуру (см. рис.)
Флаги стиля MessageBoxA
Поля структуры могут содержать следующие значения, определяющие внешний вид и поведение окна сообщения. Тип окна:
0 содержит одну кнопку OK;
1 содержит две кнопки: OK и Cancel;
2 содержит три кнопки: Abort, Retry, Ignore;
3 содержит три кнопки: Yes, No, Cancel;
4 содержит две кнопки: Yes и No;
5 содержит две кнопки: Retry и Cancel.
Поле значок содержит значение, определяющее вид отображаемой пиктограммы в окне:
0 нет значка;
1 красный кружок с крестиком (значок стоп);
2 вопросительный знак;
3 восклицательный знак;
4 кружочек с буквой i.
Поле кнопка определяет кнопку по умолчанию, т.е. ту, которая ассоциируется с нажатием на клавишу 'Enter' при появлении окна сообщения на экране:
0 1-я кнопка;
1 2-я кнопка;
2 3-я кнопка;
3 4-я кнопка.
Значение поля режим определяет способ взаимодействия окна сообщения с другими окнами. Кроме этих полей, могут быть установлены некоторые другие биты. Например, установка 14-го бита добавляет к окну сообщения кнопку 'Help'; установка 18-го бита заставляет окно все время находиться сверху и т.д.
Таким образом, число 0 в качестве стиля означает простое окно сообщения с одной кнопкой 'OK' и без всяких значков. Инструкция для помещения 0 в стек нам уже знакома: 6Ah 00h.
Вторым в стек должен быть помещен адрес начала строки, являющейся заголовком окна сообщения. Эту строку разместим в секции данных нашей программы; используя ту же схему, что и в прошлый раз, это будет третья секция, начинающаяся со смещения 3000h относительно базового адреса загрузки, который равен 400000h. В качестве заголовка можно выбрать любой текст; пусть это будет просто "Заголовок". В конце строки обязательно должен быть нулевой байт. В результате адрес нашей строки в памяти будет 403000h; поместим это непосредственное значение в стек (на этот раз мы указываем все 4 байта, поэтому бит s = 0): 68 00 30 40 00 (h).
Третьим в стек помещается адрес начала другой строки, которая является собственно выводимым сообщением. Пусть будет "Текст сообщения для вывода"; расположим ее сразу после первой строки (после ее завершающего 0) - по адресу 4030A0h: 68 A0 30 40 00 (h).
Последний параметр для функции MessageBoxA - описатель (handle) другого окна, являющегося владельцем нашего окна сообщения. Если такое окно имеется, оно (в зависимости от значения в поле "Режим") может быть "заморожено" на время отображения нашего окна сообщения. В данном случае других окон в приложении нет, поэтому оставим 0: 6A 00 (h).
Функция ExitProcess принимает единственный аргумент - т.н. код завершения. Он обычно равен 0 при нормальном завершении приложения. Мы также оставим это значение: 6A 00 (h).
Больше данных в нашем приложении не предвидится; поэтому мы можем сразу построить секцию данных. Создадим отдельную папку и разместим в ней файл "data.txt" со следующим содержимым:
Код
n data.bin
r cx
200
f 0 l 200 0
e 0 "Заголовок" 0
e a0 "Текст сообщения для вывода" 0
m 0 l 200 100
w
q
Все аргументы подготовлены; настало время рассмотреть вызовы самих функций. При нормальном ходе исполнения очередная инструкция извлекается из памяти по адресу, содержащемуся в регистре EIP. В процессе декодирования сначала определяется длина инструкции, и это значение прибавляется к содержимому регистра EIP, который, таким образом, указывает на следующую инструкцию.
Но существуют отклонения от этого последовательного хода исполнения; одним из таких случаев является вызов функций. В этом случае содержимое регистра EIP автоматически сохраняется в стеке (это значение называется адресом возврата), а в регистр EIP заносится операнд инструкции вызова функции, который является адресом первой команды вызываемой функции. В свою очередь, последней командой функции должна быть инструкция возврата управления, которая восстанавливает сохраненный ранее в стеке адрес возврата в регистре EIP, и управление переходит на следующую после вызова функции инструкцию. В результате вызов функции выглядит так, будто это обычная одиночная инструкция и не было всего этого "путешествия" куда-то в другие области адресного пространства.
Рассмотрим инструкции вызова функций (эта группа обозначается на ассемблере мнемоникой CALL). Инструкция с непосредственным смещением:
Код
11101000 <4 байта смещения>
Следующие за опкодом 4 байта являются знаковым смещением относительно адреса следующей инструкции: это значение добавляется к содержимому EIP, и полученное число является конечным адресом. Причем если самый старший бит смещения равен 1, число рассматривается как отрицательное (представленное в виде двоичного дополнения). Не вдаваясь в детали, преобразование положительных двоичных чисел в отрицательные и обратно осуществляется так: все нули исходного числа меняются на единицы, а единицы - на нули, и к этому числу добавляется единица. Например, в случае байта, 00000001 - это +1, меняем на 11111110 и добавляем 1, получая 11111111 - это будет -1 в двоичном виде.
Есть также инструкция с косвенной адресацией; она использует байт ModR/M:
Код
11111111 Mod 010 R/M
Операнд в виде адреса назначения находится в этом случае в регистре или в памяти. Обратите внимание, поскольку у этой инструкции единственный операнд (на самом деле, второй операнд - регистр EIP - задан неявно), поле REG байта ModR/M также используется в качестве расширения опкода. Более того, сам опкод (FFh) совпадает с опкодом для инструкции помещения данных в стек с байтом R/M - процессор различает эти команды как раз по полю REG байта R/M: в случае стековой инструкции это 110, а для инструкции вызова функции - 010. Забегая вперед, отметим, что здесь возможны и другие значения, создающие соответственно другие инструкции.
Хорошо; но как получить адрес нужной нам функции? Это делается посредством процесса, который называется импортом. Загрузчик Windows осуществляет его автоматически при запуске приложения, нужно только указать ему, какие функции и из каких модулей нам потребуются. Вот для этой цели и служат таблица импорта и сопутствующие ей данные, о которых упоминалось в прошлой статье. Теперь познакомимся с ними поближе.
Число записей в таблице импорта равно числу импортируемых модулей. Последняя запись должна содержать нули для обозначения конца таблицы. Каждая запись имеет следующий формат:
Смещение Размер, байт Поле
0 4 Смещение таблицы поиска
4 4 Используется для предварительного связывания; здесь - 0
8 4 Перенаправление; здесь - 0
12 4 Смещение строки с именем модуля (dll)
16 4 Смещение таблицы импортируемых адресов (IAT)
Записи этой таблицы ссылаются на другие вспомогательные таблицы и строки; их взаимоотношение показано на рисунке. Импортировать функции можно по именам или по их порядковым номерам в соответствующем модуле. Все импортируемые из одного модуля функции должны быть указаны в таблице поиска, на которую ссылается таблица импорта, следующим образом. Каждой импортируемой функции соответствует 32-разрядное значение. Если старший бит этого значения установлен (1), импорт осуществляется по номерам, и оставшийся 31 бит является значением этого номера. Если же он сброшен (0), оставшийся 31 бит является смещением (относительно базового адреса загрузки) на соответствующую строку таблицы имен. Первые две байта этой строки являются "подсказкой" загрузчику, в каком месте импортируемого модуля может находиться соответствующее имя (если "подсказки" нет, они равны 0). За ними следует сама строка с именем импортируемой функции. Таблица поиска завершается 4-байтным полем, содержащим нули.
Таблица импортируемых адресов должна находится в самом начале секции. При загрузке она должна быть идентична таблице поиска. В процессе загрузки система заполняет поля этой таблицы адресами соответствующих функций. Таким образом, мы должны указать в инструкциях вызова функций именно эти адреса.
Теперь мы можем составить нужные для нашего приложения данные. Разместим их в секции .rdata (со смещением 2000h относительно адреса загрузки). Создадим файл rdata.txt. Это как раз тот случай, когда могут оказаться полезными два прохода, чтобы узнать относительные взаимные смещения различных таблиц. Учтите, что все смещения должны указываться относительно базового адреса загрузки. Я приведу здесь уже готовый вариант:
Код
n rdata.bin
r cx
200
f 2000 l 200 0
a 2000
; 1 IAT
db 2A 20 0 0 0 0 0 0 
; 2 IAT
db 38 20 0 0 0 0 0 0
; имя 1 модуля
db "User32.dll" 0 0
; имя 2 модуля
db "Kernel32.dll" 0 0
; имя 1 функции
db 0 0 "MessageBoxA" 0
; имя 2 функции
db 0 0 "ExitProcess" 0
; таблица поиска 1
db 2A 20 0 0 0 0 0 0
; таблица поиска 2
db 38 20 0 0 0 0 0 0
; таблица импорта:
; 1 модуль
; указатель на 1 таблицу поиска
db 46 20 0 0
; 2 пустых поля
db 0 0 0 0 0 0 0 0 
; указатель на имя 1 модуля
db 10 20 0 0
; указатель на 1 IAT
db 0 20 0 0
; 2 модуль
; указатель на 2 таблицу поиска
db 4E 20 0 0
; 2 пустых
db 0 0 0 0 0 0 0 0
; указатель на имя 2 модуля
db 1C 20 0 0
; указатель на 2 IAT
db 08 20 0 0
; последняя запись - все нули

m 2000 l 200 100
w
q
Теперь мы можем закончить и кодовую секцию. Адрес функции MessageBoxA будет находиться в поле первой IAT по адресу 402000, используем в инструкции ModR/M с непосредственным смещением (Mod = 00, R/M = 101; затем следуют 4 байта адреса, где находится операнд):
Код
11111111 00 010 101 <4 байта адреса 402000h>, или FF 15 00 20 40 00.
Аналогично адрес функции ExitProcess будет по адресу 402008, а инструкция выглядит так: FF 15 08 20 40 00.
Составим файл code.txt:
Код
n code.bin
r cx
200
f 100 l 200 0
a 100
; помещаем в стек параметры MessageBoxA
db 6a 00
db 68 00 30 40 00
db 68 a0 30 40 00
db 6a 00
; вызываем MessageBoxA
db ff 15 00 20 40 00
; помещаем в стек параметр (0)
db 6a 00
; вызываем ExitProcess
db ff 15 08 20 40 00

w
q
Готово почти все; единственное, что осталось - подправить PE-заголовок в нашем шаблоне. Скопируем файл Header.txt, созданный в прошлый раз, в рабочий каталог. Теперь в нашем приложении есть таблица импорта, и надо указать ее смещение (2056h) и размер (3Ch) в каталоге. Найдите в файле Header.txt строку "; Здесь начинается первый элемент каталога:". Теперь переделайте его начало следующим образом:
Код
; смещение таблицы экспорта (4 байта)
db 0 0 0 0
; размер таблицы экспорта (4 байта)
db 0 0 0 0
; Второй элемент каталога:
; смещение таблицы импорта (4 байта)
db 56 20 0 0
; размер таблицы импорта (4 байта)
db 3c 0 0 0
Все! Пишем заключительный файл сборки (make.bat):
Код
@echo off
debug < header.txt > report.lst
debug < code.txt >> report.lst
debug < rdata.txt >> report.lst
debug < data.txt >> report.lst
copy /b header.bin+code.bin+rdata.bin+data.bin msg.exe
Запустив make.bat, мы получим файл msg.exe. Прежде чем запускать его, внимательно проверьте файл отчета report.lst на предмет сообщений об ошибках. Всего один неверно введенный символ (например, русская буква е в команде e) вызовет ошибку, отладчик не выполнит соответствующую команду, в результате создаваемая нами структура окажется неверной, что может привести к совершенно неожиданным результатам и даже вызвать сбой всей системы (особенно если это ошибка в PE-заголовке).
Если же все нормально - хлопайте в ладоши! Вот оно, наше окно, собственноручно созданное самым честным образом в самых что ни на есть настоящих машинных кодах.
Фрагмент из книги Ивана Склярова "Головоломки для хакеров"
Создать СОМ-файл (в том числе и вирус) в системе отключенной от сети можно с помощью простейшего консольного отладчика debug.exe, который появился уже в первых версиях MS-DOS и стандартно устанавливается во всей линейке Windows от 9x до 2003. Рассмотрим, как им можно воспользоваться на примере простой программы, которая выводит на экран фразу: "Hello, World!". Чтобы ввести эту программу с помощью debug.exe и создать исполняемый файл в системе, нужно выполнить следующие действия:
Код
debug.exe
-a
0B27:0100 mov ah,9
0B27:0102 mov dx,108
0B27:0105 int 21
0B27:0107 ret
0B27:0108 db "Hello, World!$"
0B27:0116
-r cx
CX 0000
:16
-n world.com
-w
Запись 00016 байт
-q
Некоторые комментарии к происходящему. Сначала запускается debug.exe, затем командой a (assemble) включается режим ассемблирования и вводится программа. После последней команды RET вводятся данные, затем дважды нажимается клавиша <Enter> для выхода из режима ассемблирования и вводится команда r cx (register) для изменения содержания регистра CX, при этом будет показано существующее значение. В CX необходимо указать длину создаваемого COM-файла. Поскольку программа начинается с адреса 0100h, а заканчивается 0116h, то простым вычитанием определяется общая длина файла: 0116h-0100h =16h=22. Именно это новое значение вводится после двоеточия. Далее командой n (name) указывается название файла (в нашем случае это world.com) и командой w (write) осуществляется запись на диск, при этом debug покажет количество записанных байтов ("Запись 00016 байт"). Команда q (quit) выполняет выход из отладчика.
5
Ушел с форума
Автор FAQ
15840 / 7422 / 994
Регистрация: 11.11.2010
Сообщений: 13,385
30.05.2013, 11:51  [ТС] 7
Графика на ассемблере под DOS
Представление данных на мониторе компьютера в графическом виде впервые было реализовано в середине 50-х годов для больших ЭВМ, применявшихся в научных и военных исследованиях. С тех пор графический способ изображения данных стал неотъемлемой принадлежностью подавляющего числа компьютерных систем, в особенности персональных.
MDA
Monochrome Display Adapter был создан для работы с одноцветным (зеленым) дисплеем. Адаптер дисплея преобразует сигналы, распространяющиеся по шине компьютера, к форме, воспринимаемой монитором. MDA является символьной системой, не обеспечивающей никакой другой графики, за исключением расширенного множества символов IBM (символы псевдографики). MDA работал только в одном режиме — вывод 25 строк символов по 80 символов в ряду. Вы помещали символы на экран, записывая коды ASCII в дисплейный буфер. Преобразование из кода ASCII в точки на экране выполнялось аппаратно. Для вывода символа размером 9х14 точек требовалось разрешение (80х9)х(14х25) = 720х350 = 252000 точек на экран. На плате MDA располагалось 4 Кб памяти для записи кодов символов и их атрибутов.
CGA
Color Display Adapter — первый растровый многорежимный дисплейный адаптер. Использовался и для символьных, и для графических режимов. Содержал 16 Кб памяти, соединяется с монитором 4 сигнальными проводами (синий, красный, зеленый, яркостный), и поэтому мог работать одновременно с 24=16 цветами. Позволяет работать в двух текстовых (монохромный — 25 строк по 80 символов в строке и 16-цветный — 25 строк по 40 символов в строке) и трех графических режимах.
графические режимы CGA
разрешение
экрана
Количество цветов
Низкая разрешающая способность160х200 16 цветов
Средняя разрешающая способность320х200 4 цвета из 4 цветовых наборов
Высокая разрешающая способность640х200 2 цвета (черный и 1 из 15 возможных)
EGA
Enhanced Display Adapter — улучшенный графический адаптер, выпущен в 1984 году. Он снабжался от 64 до 256 Кб памяти. Позволяет одновременно работать с 26=64 цветами. Яркость изображения на экране определяется уровнем напряжения видеосигнала. Адаптер соединялся с монитором 6 сигнальными проводами (синий, синий вспомогательный, красный, красный вспомогательный, зеленый и зеленый вспомогательный). Внутри цветного монитора три ЦАПа (цифро-аналоговых преобразователя) позволяли получить из 2-х цифровых сигналов для каждого цвета по 4 (22=4) яркостных градации, которые отправлялись на три цветовые пушки монитора. Адаптер EGA обеспечивает работу монохромного дисплея в графическом режиме, поддерживает все текстовые режимы CGA, графические режимы средней и высокой разрешающей способности CGA (режимы 04 и 05) и к ним добавляются еще 4 собственных графических и 1 текстовый режим.
VGA
Video Graphics Array содержит 256 Кб до 1 Мб памяти. Позволяет выводить на экран 218=262144 цветовых оттенка, но одновременно на экране могут быть только 256 цветов. Имеет три встроенных ЦАПа. На монитор VGA адаптер отправляет три аналоговых сигнала, которые управляют работой электронных пушек монитора. Поддерживает 17 документированных режимов: 640х480 (монохром), 640х480х16 цветов, 320х200х256 цветов (смотри таблицу ниже) и кучу недокументированных, но также стандартных режимов: 320х400х256 цветов, 360х480х256 цветов и т.д.
Некоторые стандартные видеорежимы
Название
режима
Тип
Разрешающая
способность
Количество
цветов
Адаптер
00 текстовый 320x200 16 CGA
01 текстовый 320x200 16 CGA
02 текстовый 640x200 16 CGA
03 текстовый 720x400 16 CGA
04 графический 320x200 4 CGA
05 графический 320x200 4 цвета из 4 наборов CGA
06 графический 640x200 2 CGA
07 текстовый 720х350 3 (b/w/bold) MDA, EGA
0Dh графический 320x200 16 EGA
0Eh графический 640x200 16 EGA
0Fh графический 640x350 3 (b/w/bold) EGA
10h графический 640x350 16 EGA
11h графический 640x480 2 VGA
12h графический 640x480 16 VGA
13h графический 320x200 256 VGA
Нетрудно подсчитать, что режим 640х400х256 цветов использует практически всю 256 Кбайтовую память VGA адаптера (640х400х8 = 2048000 бит = 256000 байт = 250 Кбайт). В то же время многие VGA адаптеры снабжены, как правило, не менее чем 1 Мбайт видеопамяти, а большинство находящихся в эксплуатации имеют не менее 512 Кбайт, что позволяет нам получить в свое распоряжение режимы 640х480х256 цветов (300 Кбайт) и 800х600х256 цветов (469 Кбайт). К большому сожалению, адаптер VGA не поддерживает глубину цвета более 8 разрядов (28=256 цветов).
Super VGA
К моменту кончины DOS самый распространенный видеоадаптер. Содержит от 1 Мб и более памяти. Позволяет выводить на экран изображение с максимальным разрешением 1600х1200 и максимальным цветовым разрешением 16 777 216 оттенков. Адаптеры CGA, EGA и VGA программно совместимы всегда и совместимы снизу вверх: программа, написанная для CGA будет работать на EGA, VGA и SVGA, написанная для EGA будет работать на VGA и SVGA
Видеоадаптеров SVGA достаточно много и не все они программно совместимы между собой. По-этому для использования в программе видеорежимов Super-VGA необходимо либо производить настройку на конкретный видеоадаптер, либо использовать видеоадаптер в соответствии со стандартом VESA. Функции VESA SVGA вызываются как подфункции 4Fh прерывания 10h. Номер функции 4Fh должен находится в регистре AH, номер подфункции (номер функции VESA) должен находится в регистре AL
Видеорежимы SVGA по стандарту VESA
Номер
видеорежима
Разрешение
Количество
цветов
100h 640x400 256
101h 640x480 256
102h 800x600 16
103h 800x600 256
104h 1024x768 16
105h 1024x768 256
106h 1280x1024 16
107h 1280x1024 256
10Dh 320x200 32768
10Eh 320x200 65536
10Fh 320x200 16777216
110h 640x480 32768
111h 640x480 65536
112h 640x480 16777216
113h 800x600 32768
114h 800x600 65536
115h 800x600 16777216
116h 1024x768 32768
117h 1024x768 65536
118h 1024x768 16777216
119h 1280x1024 32768
11Ah 1280x1024 65536
11Bh 1280x1024 16777216
Видеорежимы
Обобщаем. Видеорежимы бывают текстовые и графические, различаются разрешением экрана и количеством цветов, одновременно выводимых на экран. В текстовом режиме в видеопамяти находятся коды символов и их атрибуты, которые из таблицы символов выводятся на экран монитора. В графическом режиме в видеопамяти находится код цвета каждой точки, отображаемой на экране.
Видеорежимы меняются из программ с помощью вызова функции BIOS. Все функции BIOS вызываются с помощью прерывания 10h. Значения регистров при вызове функции смены видеорежима:
AH=0 — номер функции BIOS;
AL — номер видеорежима, который нужно включить.
Фрагмент программы, в котором устанавливается видеорежим 13h.
Assembler
1
2
MOV AX, 0013h ; AH=0 AL=13h
INT 10h
Структура видеопамяти в 256-цветных видеорежимах
Одни из самых простых в программировании графики режимов 256-цветные. Для VGA это единственный режим 13h, у SVGA несколько таких режимов. Программирование 256-цветных режимов SVGA незначительно отличается от режима 13h. Основное отличие связано с тем, что не вся видеопамять, отображаемая на экране, одновременно доступна, и приходится заполнять видеопамять частями.
Y\Xhttps://www.cyberforum.ru/cgi-bin/latex.cgi?\rightarrow01...319
00000000001...00319
10032000321...00639
...............
1996368063681...63999
В 256-цветных режимах каждой точке соответствует один байт в видеопамяти. Видеопамять начинается в этих режимах с адреса A000h:0000h. Первый байт в видеопамяти соответствует левой верхней точке экрана. Второй байт соответствует второй слева точке в верхней строке экрана. После байта, соответствующего последней точке в верхней строке, идет байт соответствующий первой точке второй строки. Счет идет слева направо и сверху вниз.
Адрес точки для всех 256-цветных режимов вычисляется следующим образом:
  • сегмент экрана для всех точек равен 0A000h;
  • начало координат лежит в левом верхнем углу;
  • Y — вертикальная координата — увеличивается вниз;
  • X — горизонтальная координата — увеличивается вправо;
  • H — горизонтальное разрешение экрана;
  • смещение (адрес) любой точки на экране определяется по формуле A=Y*H+X.
Палитра
Из 262144 (=218) цветовых оттенков, которые может создать стандартная VGA-карта, на экран выводятся одновременно всего лишь 256 цветов. Почему? VGA адаптер имеет встроенный Цифро-Аналоговый Преобразователь (ЦАП), который содержит 256 регистров цвета. Величина каждого регистра 18 бит. Из этих 18 бит на красный цвет отводится 6 бит, 6 бит — на зеленый цвет и 6 бит — на синий (218=262144). Номер цвета в видеопамяти — это номер регистра цвета, а цвет точки зависит от значения, хранящегося в этом регистре.
Выбрать палитру — значит установить нужные значения в регистрах цвета. Для установки одного регистра цвета используют 10h подфункцию 10h функции BIOS. Значения регистров при вызове функции установки регистра цвета следующие:
  • AH=10h номер функции;
  • AL=10h номер подфункции;
  • BX — номер устанавливаемого регистра;
  • DH — яркость красного (от 0 до 3Fh);
  • CH — яркость зеленого (от 0 до 3Fh);
  • CL — яркость синего (от 0 до 3Fh).
Для установки нескольких или всех регистров цвета используют 12h подфункцию 10h функции BIOS. Перед вызовом этой функции в памяти создают таблицу палитры, имеющую следующий формат: яркость красного первого устанавливаемого цвета, яркость зеленого первого цвета, яркость синего первого цвета, яркость красного второго цвета и т.д. Значения таблицы должны находиться в пределах от 0 до 3Fh. Значения регистров при вызове функции установки регистра цвета:
  • AH=10h номер функции;
  • AL=12h номер подфункции;
  • BX — номер первого устанавливаемого регистра;
  • CX — количество устанавливаемых регистров;
  • ES: DX — адрес таблицы палитры.
В VGA, даже в текстовых режимах с 16 цветовыми оттенками, вывод цвета осуществляется через ЦАП. Привожу небольшую программу, устанавливающую первый регистр цвета (изначально синий) в другое значение. Скомпилируйте и запустите эту программу из оболочки использующей синий цвет, например, Norton Commander, Far или Волков Commander и Вы увидите, что синий цвет окон изменился на розовый:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
.286
.MODEL TINY
.CODE
ORG 100h
start: MOV AX,1010h ;номер функции
MOV BX,1 ;выбираем первый регистр
MOV DH,3Fh ;яркость красной компоненты
MOV CH,01Fh ;яркость зеленой компоненты
MOV CL,01Fh ;яркость синей компоненты
INT 10h ;установить нужное значение в
;регистре ЦАП
RET ;выход из программы
END start
Программирование графики
В основе любой графической функции лежит несколько простых графических функций, называемых графическими примитивами. Такими примитивами являются прорисовка точек, линий, окружностей, а также заполнение областей и перемещение битовых образов.
Точка
Проще всего записать точку на экран в любом графическом режиме вызвав функцию 0Bh прерывания 10h. Содержание регистров при вызове функции: AH=0Bh, в AL цвет пикселя (если бит 7=1 выполняется операция логического исключающего ИЛИ с цветом экрана), в BH номер страницы, в CX — X координата пикселя, в DX — Y координата пикселя.
Если Вы хотите писать напрямую в видеопамять, то например установка точки в режиме 13h сводится к записи кода цвета в видеопамять по адресу, соответствующему адресу точки A=Y*H+X. Ниже приводится программа, рисующая три точки разными цветами в разных местах экрана. Подпрограмма Draw_pixel выводит точку с заданными координатами и с заданным цветом. При вызове данной подпрограммы в регистре BX должна находиться X координата точки, в регистре CL — Y координата, в регистре DL — цвет точки.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
.286
.model tiny
.code
ORG 100h
start: MOV AH,0Fh ;запомнить текущий видеорежим
INT 10h
MOV VIDEOR,AL;видеорежим в переменную videor
MOV AX, 0013h ;установить видеорежим 13h
INT 10h
PUSH 0A000h ;установить регистр ES на сегмент
POP ES ;видеопамяти
MOV DI,0 ;Установка точки (0, 0) зеленого цвета
MOV CX,0 ;в левый верхний угол экрана
MOV DL,2
CALL DRAW_PIXEL
MOV DI,160 ;Установка точки (160,100) красного
MOV CX,100 ; цвета в центр экрана
MOV DL,4
CALL DRAW_PIXEL
MOV DI,319 ;Установка точки (319, 199) белого
MOV CX,199 ; цвета в правый нижний угол экрана
MOV DL,7
CALL DRAW_PIXEL
XOR AX,AX ;ожидание нажатия любой клавиши
INT 16h
MOV AH,0 ;восстановление видеорежима
MOV AL,VIDEOR
INT 10h
ret ;выход из программы
;процедура вывода точки на экран
;DI — X координата точки (от 0 до 319)
;CX — Y координата точки (от 0 до 199)
;DL — цвет точки
PROC NEAR DRAW_PIXEL
;присвоить регистру AX значение горизонтального
;разрешения
MOV AX,320 ;умножение координаты Y на горизонтальное
MUL CX ;разрешение. Результат умножения в AX
MOV BX,AX ;сложить с координатой X
MOV ES:[BX][DI],DL;вывести точку на экран
RET ;выход из процедуры
ENDP ;конец процедуры
VIDEOR DB ? ;переменная для хранения значения
;текущего видеорежима
END start;конец программы
На примере этой программы можно выводить на экран различные графики.
Рисование линий на экране
Проведение линий подразумевает установку на экране всех точек, принадлежащих отрезку. Сложность при рисовании линии в том, что точки из которых мы ее строим создавали иллюзию прямой. На экране абсолютно точно можно нарисовать только вертикальные, горизонтальные и 1:1 диагональные линии.
Если у нас установлен 13h графический режим (320x200x256), то горизонтальную линию рисуют следующими командами
Assembler
1
2
3
4
5
6
7
8
9
10
11
;предварительные установки
PUSH 0A000h
POP ES; позиционируем ES на область видеопамяти
MOV DI,X ; в DI координаты начальной точки по X
MOV AX,320; длина строки экрана
MUL Y; умножаем на Y
ADD DI,AX; и складываем с X
MOV AL,COLOR; цвет линии
; рисуем горизонтальную линию
MOV CX,N; длина линии
REP STOSB
Вертикальную линию обычно рисуют циклом
Assembler
1
2
3
4
MOV CX,N; длина линии
A1: MOV ES:[DI],AL; рисуем точку на строке
ADD DI,320; переход на следующую строку
LOOP A1
диагональную линию с наклоном влево можно нарисовать циклом
Assembler
1
2
3
4
MOV CX,N; длина линии
A1: MOV ES:[DI],AL; рисуем точку на строке
ADD DI,319; переход на следующую строку
LOOP A1
диагональную линию с наклоном вправо — циклом
Assembler
1
2
3
4
MOV CX,N; длина линии
A1: MOV ES:[DI],AL; рисуем точку на строке
ADD DI,321; переход на следующую строку
LOOP A1
Первое, все остальные линии рисуются только приближенно. Второе, что бы быть полезной, функция рисования линии должна работать быстро.
Давайте рассмотрим случай, когда горизонтальная проекция линии длиннее вертикальной и угол наклона линии отрицательный. Например, нарисуем линию направленную из точки (0, 0) в точку (55, 12). Программа должна провести линию в 55 пикселя по горизонтали и 12 пикселей по вертикали.
Поскольку наклон линии находится в пределах 55/12=4,5833…, то можно точно сказать что линия будет состоять из чередующихся рядов точек (прогонов) из 4 (минимальный прогон) и 5 точек (максимальный прогон). Как разместить прогоны с минимальной и максимальной длиной? Остаток деления 55 на 12 равен 7, равномерно раскидаем эти дополнительные 7 точек. На каждый шаг по оси Y мы ставим, по меньшей мере, 4 пикселя вдоль оси X. После этого нам нужно решить, ставить ли пятый или переходить на следующую координату по оси Y. Если мы подсчитаем, какую ошибку отклонения мы накопили, и если ошибка накопления превышает 0,5 пикселя — тогда нам нужен в отрезке дополнительный пиксель. Длина минимального отрезка по оси X на один шаг по оси Y составляет XDelta/YDelta=4,5833… Обозначим XDelta%YDelta получение остатка от деления XDelta на YDelta, ошибка накопления в каждом прогоне составляет (XDelta%YDelta)/YDelta=7/12=0,5833… дополнительный пиксел потребуется если (XDelta%YDelta)/YDelta — ½ > 0
(XDelta%YDelta) — YDelta /2 > 0
(XDelta%YDelta)х2 — YDelta > 0
то есть, на каждом шаге по оси Y накапливается ошибка (XDelta %YDelta)x2. Если ошибка достигнет одного пикселя или больше, то мы добавим к прогону дополнительный пиксель и вычтем из значения ошибки YDelta*2
Теперь попробуем нарисовать линию — на каждый шаг по оси Y будем ставить по 4 или 5 точек по оси X. Если соединить центры наших прогонов, то окажется, что наша линия смещена на 2 или 3 точки от идеальной линии. Чтобы точки максимально близко ложились к идеальной линии применяется балансировка прогонов. Для этого берут один максимальный прогон и распределяют его равномерно между первым и последним отрезком, чтобы концы линии стали симметричными. Ниже приводится листинг программы реализующее построение линии по алгоритму Брезенхейма с переменной длиной отрезков.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
.286
.model tiny
.code
org 100h
start:
SCREEN_WIDTH equ 320;ширина экрана в режиме 13h
SCREEN_SEGMENT equ 0A000h
parms struc
dw ? ; сохраненный в стеке ВР
dw ? ; сохраненный в стеке адрес возврата
XStart dw ? ; начальная координата Х линии
YStart dw ? ; начальная координата Y линии
XEnd dw ? ; конечная координата Х линии
YEnd dw ? ; конечная координата Y линии
Color db ? ;цвет линии, рядом с ним пустой байт,
db ? ;потому что Color в стеке длиной в слово
parms ends
; Локальные переменные.
AdjUp equ -2;ошибку накопления подправляем на каждом шаге
AdjDown equ -4;ошибку накопления уменьшаем, когда превышен порог
WholeStep equ -6 ; минимальная длина прогона
XAdvance equ -8 ;1 или -1, направление, в котором идем по оси Х
LOCAL_SIZE equ 8
mov ah,0Fh ;запомнить видеорежим
int 10h
mov videor,al
mov ax,13h
int 10h;установили видеорежим 256x320x200
push 5 ;Color
push 12 ;YEnd
push 55 ;XEnd
push 0 ;YStart
push 0 ;XStart
call LineDraw ;рисуем линию
mov ah,0 ;ждем нажатия на клавишу
int 16h
mov ax,word ptr videor ;восстановить видеорежим
int 10h
int 20h ;выход из программы
videor db 0,0
LineDraw proc near;универсальная процедура для рисования линий
push bp ; сохраним стек вызывающей программы
mov bp,sp ;спозиционируемся на стек
sub sp,LOCAL_SIZE ; выделим место для локальных переменных
push si ; сохраним регистровые переменные
push di
push ds ; сохраним DS
;Рисуем сверху вниз,чтобы уменьшить число сравнений и
;чтобы получить одни и те же точки линий с
;одинаковыми координатами.
mov ax,[bp].YStart
cmp ax,[bp].YEnd
jle LineIsTopToBottom
xchg [bp].YEnd,ax;поменяем местами координаты линии
mov [bp].YStart,ax
mov bx,[bp].XStart
xchg [bp].XEnd,bx
mov [bp].XStart,bx
LineIsTopToBottom:
mov dx,SCREEN_WIDTH ;Установим DI на первый пиксель
mul dx ;для отображения.YStart*SCREEN_WIDTH
mov si,[bp].XStart
mov di,si ; DI=YStart*SCREEN_WIDTH+XStart
add di,ax ; в DI смещение первого пикселя
; Определим, до каких пор идти по вертикали.
mov cx,[bp].YEnd
sub cx,[bp].YStart ;СХ = YDelta
;Определим, идти влево или вправо и до каких пор идти
;по горизонтали. По ходу дела выделим рисование
;вертикальных линий в целях ускорения, а также во
;избежание предельных случаев и деления на 0.
mov dx,[bp].XEnd
sub dx,si ;XDelta
jnz NotVerticalLine;XDelta = 0 означает
;вертикальную линию
push SCREEN_SEGMENT;установим DS:DI на первый
pop ds ;байт для отображения
mov al,[bp].Color
VLoop: mov [di],al
add di,SCREEN_WIDTH
loop VLoop
jmp Done
; Обработка горизонтальных линий.
IsHorizontalLine: push SCREEN_SEGMENT;установим
pop es ;ES:DI на первый байт для отображения
mov al,[bp].Color; сдублируем старший байт
mov ah,al ;для пословного вывода
and bx,bx ; слева направо?
jns DirSet ;да
sub di,dx ; обработка движения справа налево,
; спозиционируемся на левый край линии
DirSet: mov cx,dx
inc cx ; число пикселей для отображения
shr cx,1 ;число слов для отображения
rep stosw ; обработаем как можно больше слов
adc cx,cx
rep stosb; обработаем нечетный байт, если
jmp Done; таковой имеется
; Обработка диагональных линий.
IsDiagonalLine:
push SCREEN_SEGMENT;установим DS:DI на первый
pop ds ;байт для отображения
mov al,[bp].Color
add bx,SCREEN_WIDTH ;пройдем расстояние от
; одного пикселя до следующего
DLoop: mov [di],al
add di,bx
loop DLoop
jmp Done
NotVerticalLine: mov bx,1;начинаем слева направо,
; так что XAdvance=1 флаги не изменяются
jns LeftToRight ; проход слева направо
neg bx;проход справа налево, так что
;XAdvance = -1
neg dx ; модуль XDelta
LeftToRight: ; Обработка горизонтальных линий.
jcxz IsHorizontalLine ;YDelta = 0? да
; Обработка горизонтальных линий.
   cmp cx,dx ; YDelta = XDelta?
   jz IsDiagonalLine ;да
;Определим, какая из осей основная, а какая вспомогательная.
   cmp dx,cx
   jb YMajor;Линия с основной осью Х (горизонтальная проекция больше вертикальной).
XMajor: push SCREEN_SEGMENT; установим ES:DI на
   pop es ;первый байт для отображений
   and bx,bx ;слева направо?
   jns DFSet ;да, CLD уже установлен
   std ;справа налево, так что рисуем в обратном направлении
DFSet: mov ax,dx ;XDelta
   sub dx,dx; подготовим для деления
   div cx ;AX = XDelta/YDelta;(минимальное число пикселей в прогоне этой линии) ;DX=XDelta%YDelta
   mov bx,dx;ошибку накопления подправляем при каждом шаге по оси Y
   add bx,bx ;используется для индикации, не нужен ли дополнительный пиксель в прогоне, чтобы округлить
   mov [bp].AdjUp,bx;нецелые шаги вдоль оси Х при 1-пиксельных шагах вдоль оси Y
   mov si,cx;ошибку накопления подправляем, когда ее
   add si,si ;значение превышает допустимый порог
;используем это для определения, необходимо ли сделать шаг по оси Х
   mov [bp].AdjDown,si ;Начальная ошибка накопления отражает начальный шаг 0,5 вдоль оси Y.
   sub dx,si ;(XDelta % YDelta) - (YDelta * 2), DX - начальная ошибка накопления
;Первый и последний прогоны являются неполными, потому что перемещение по оси Y идет на 0,5,а не
;на 1.Разделим один полный прогон плюс начальный пиксель между первым и последним прогонами.
   mov si,cx ;SI = YDelta
   mov cx,ax ;шаг (минимальная длина прогона)
   shr cx,1
   inc cx ;счетчик начального пикселя=(шаг/2)+1 (подправим позже).
;Это также счетчик пикселей в последнем прогоне
   push cx;запомним счетчик пикселей в последнем прогоне. Если основная длина прогона четная, а
;дробная часть отсутствует, то у нас есть бесхозный пиксель, который можно определять либо
;в первый, либо в последний прогон, вот давайте и определим этот пиксель в последний прогон. Если
;число пикселей в прогоне нечетно, то у нас есть пиксель, который нельзя определить ни в первый,
;ни в последний прогон, так что добавим, 0,5 к ошибке накопления чтобы текущий пиксель
;обрабатывался стандартным циклом рисования прогона.
   add dx,si ;положим нечетную длину, добавим
;YDelta к ошибке накопления (добавим 0,5 пикселя к ошибке накопления)
   test al,1 ; четна ли длина прогона?
   jnz XMajorAdjustDone;нет, и тогда все уже готово
   sub dx,si ; длина четна поэтому проверим ошибку,.
   and bx,bx ; 0 или нет?
   jnz XMajorAdjustDone ;нет (проверять длину на четность смысла нет, так как только что это уже
   dec cx; было проделано) оба условия удовлетворены, делаем прогон 1 короче
XMajorAdjustDone:
   mov [bp].WholeStep,ax ;шаг (минимальная длина прогона)
   mov al,[bp].Color;AL – цвет, рисуем первый прогон пикселей.
   rep stosb ; рисуем последний прогон
   add di,SCREEN_WIDTH;перейдем вдоль неосновной оси (Y)
; Рисуем все прогоны.
   cmp si,1 ;есть ли более чем 2 сканирования,
; т.е. нет ли заполненных прогонов? (SI = число сканирований - 1)
jna XMajorDrawLast ;заполненных прогонов нет
   dec dx ;подправим ошибку накопления на -1,
; чтобы использовать проверку флага переноса
   shr si,1
jnc XMajorFullRunsOddEntry ;если число
;сканирований, нечетное -- выполняем нечетное сканирование
XMajorFullRunsLoop:
   mov cx,[bp].WholeStep;прогон не может быть короче этой величины
   add dx,bx ;обновим ошибку накопления и добавим
   jnc XMajorNoExtra;дополнительный пиксель, если требуется
   inc cx
   sub dx,[bp].AdjDown;сбросим ошибку накопления
XMajorNoExtra: rep stosb;рисуем прогон этой линии сканирования
   add di,SCREEN_WIDTH ;перейдем вдоль неосновной оси (Y)
XMajorFullRunsOddEntry: ;если число нечетно, войдем в цикл заполнения прогонов
   mov cx,[bp].WholeStep ;прогон не может быть короче этой величины
   add dx,bx ; обновим ошибку накопления и добавим
   jnc XMajorNoExtra2 ; дополнительный пиксель, если того требует ситуация
   inc cx
   sub dx,[bp].AdjDown ;сбросим ошибку накопления
XMajorNoExtra2: rep stosb ; рисуем прогон этой линии сканирования
   add di,SCREEN_WIDTH;перейдем вдоль неосновной оси (Y)
   dec si
   jnz XMajorFullRunsLoop
; Рисуем последний прогон пикселей.
XMajorDrawLast: pop cx ;возьмем длину последнего прогона
   rep stosb ;рисуем последний прогон
   cld ;восстановим флаг нормального направления
   jmp Done
;Y - основная ось (вертикальная проекция больше
;горизонтальной).
YMajor: mov [bp].XAdvance,bx ;запомним, в какую
; сторону идти по оси Х
   push SCREEN_SEGMENT;установим DS:DI на
   pop ds ;первый байт для отображения
   mov ax,cx ; YDelta
   mov cx,dx ; XDelta
   sub dx,dx ;подготовим для деления
   div cx ;AX = YDelta/XDelta
;(минимальное число пикселей в прогоне этой линии) DX = YDelta % XDelta
   mov bx,dx ;ошибку накопления подправляем
; каждый раз при шаге вдоль оси X
add bx,bx ;ошибку накопления используем для
mov [bp].AdjUp,bx ; индикации не пора ли
; добавить еще пиксель к базовой длине прогона?
;чтобы округлить ошибку отклонения при шагах
mov si,cx ; вдоль оси Y,ошибку накопления
add si,si ; подправим, когда она переполнится
;и скажет,что пора сделать шаг по Y
mov [bp].AdjDown,si ;Начальная ошибка
;накопления, отражает начальный шаг величиной 0,5
;вдоль оси X.
sub dx,si ;DX=(YDelta%XDelta)-(XDelta*2)
; - начальная ошибка накопления Первый и
; последний прогоны являются неполными, потому
;что для них ось Х изменяется только на 0,5, а
;не на 1. Разделим один полный прогон плюс
;начальный пиксель между первым и последний
;прогонами.
mov si,cx ;SI = XDelta
mov cx,ax ;шаг (минимальная длина прогона)
shr cx,1
inc cx ;счетчик начального пикселя
; =(whоlеstер/2)+1 (подправин позже)
push cx ;запомним счетчик пикселей в
; последнем прогоне. Если основная длина прогона
; четная, а дробная часть отсутствует, у нас есть
;пиксель, который нужно отправить либо в первый,
; либо в последний прогон, поэтому отправим его в
; последний прогон. Если число пикселей в прогоне
; ненечетно, то один пиксель нельзя добавить ни к
;первому, им к последнему прогону, так что
; добавим 0,5 к ошибке накопления, чтобы текущий
;пиксель обрабатывался стандартным циклом
;рисования прогона.
add dx,si ;положим, что длина нечетная,
; добавим Xdelta к ошибке накопления
test al,1 ;длина прогона четная?
jnz YMajorAdjustDone ;нет. а значит, все уже
;сделано
sub dx,si ;длина четная, сделаем все заново
and bx,bx ; ошибка накопления равка О?
jnz YMajorAdjustDone ;нет (не нужно проверять
; длину на четность, потому что это уже
dec cx ; проделано)оба условия удовлетворены;
; сделаем первый прогон на 1 короче
YMajorAdjustDone: mov [bp].WholeStep,ax
;полный шаг (минимальная длина прогона)
mov al,[bp].Color ;AL - цвет
mov bx,[bp].XAdvance;направление движения вдоль оси Х
;Рисуем первый, неполный прогон пикселей.
YMajorFirstLoop: mov [di],al ; рисуем пиксель
add di,SCREEN_WIDTH ;перейдем по основной оси (Y)
loop YMajorFirstLoop
add di,bx ; перейдем вдоль неосновной оси (X)
;Рисуем все прогоны.
cmp si,1;число полных прогонов.Если всего
;прогонов более двух, значит, имеются полные
;прогоны? (SI = число прогонов - 1)
jna YMajorDrawLast ; полных прогонов нет
dec dx ;подправим ошибку накопления на -1,
; чтобы использовать проверку флага переноса.
shr si,1;считаем пары прогонов на основе
; пройденных единичных прогонов. если число
; прогонов нечетно, обрабатываем
jnc YMajorFullRunsOddEntry ;сейчас нечетный прогон
YMajorFullRunsLoop: mov cx,[bp].WholeStep ;прогон не
;может быть короче этой величины обновим ошибку
;накопления и добавим
add dx,[bp].AdjUp ;дополнительный пиксель,
jnc YMajorNoExtra ;если ошибка накопления
;просит об этом
inc cx ; дополнительный пиксель в прогоне
sub dx,[bp].AdjDown;сбросим омибку накопления
YMajorNoExtra: ;Рисуем
YMajorRunLoop: mov [di],al ;рисуен пиксель
add di,SCREEN_WIDTH;перейдем по основной оси (Y)
loop YMajorRunLoop
add di,bx ;перейдем вдоль неосновной оси (X)
YMajorFullRunsOddEntry: ;войдем здесь в цикл.Если
mov cx,[bp].WholeStep ;число прогонов
;нечетно, прогон не может быть короче этой
;величины.
add dx,[bp].AdjUp ;обновим ошибку накопления
jnc YMajorNoExtra2; и добавим дополнительный
;пиксель, если ошибка накопления просит об этом
inc cx ;дополнительный пиксель в прогоне.
sub dx,[bp].AdjDown;с6росим ошибку накопления
YMajorNoExtra2: ; Рисуем
YMajorRunLoop2: mov [di],al ;рисуем пиксель
add di,SCREEN_WIDTH;перейдем по основной оси (Y)
loop YMajorRunLoop2
add di,bx ;перейдем вдоль неосновной оси (X)
dec si
jnz YMajorFullRunsLoop
; Рисуем последний прогон пикселей.
YMajorDrawLast:
pop cx ;возьмем длину последнего прогона пикселей
YMajorLastLoop: mov [di],al ;рисуем пиксель
add di,SCREEN_WIDTH ;перейдем по основной оси (Y)
loop YMajorLastLoop
Done: pop ds ;восстановим DS
pop di
pop si ;восстановим регистровые переменные
mov sp,bp ;освободим локальные переменные
pop bp;восстановим стек вызывающей программы
ret
LineDraw endp
end start
Рисование окружности
Построить окружность несложно. У окружности координаты любой точки относительно ее центра вычисляются из соотношения R2=X2+Y2, где R радиус окружности. С точки зрения программирования достаточно нарисовать 1/8 часть окружности, а симметрия закончит дело. В языках программирования высокого уровня существует специальная функция CIRCLE, которая строит окружность либо, вычисляя синус, либо, двигаясь например по оси X вычисляет на каждом шаге координату Y по формуле https://www.cyberforum.ru/cgi-bin/latex.cgi?\sqrt{R^{2}-X^{2}}. В языке ассемблера для вычисления квадратного корня или функции синуса пришлось бы использовать обращение к сопроцессору, а такая программа с точки зрения ассемблера работает непозволительно долго. Подумаем, как нам реализовать более быстрый метод рисования окружностей используя только целочисленную арифметику, ведь на экране можно выводить точку только туда, где находится люминофор, а не между люминофорами, или смещать точку на долю микрона вправо или влево. Пусть центр окружности находится в точке (0, 0) Y=R=100 и X=0 по формуле Y2 равен R2-X2. По мере движения по оси X мы должны выяснить, когда нам необходимо уменьшить Y. Это нужно сделать если отклонение от Y будет больше 0,5 величины люминофора, т.е. больше должна засвечиваться соседняя точка. Вычисляем квадрат отклонения: (Y-0,5)2=Y2-Y+0,25. Выражение Y2-Y вычисляется в целых числах, а 0,25 игнорируем. Если разность R2-X2 больше чем Y2-Y необходимо уменьшить Y на единицу и опять пересчитать ту величину, когда необходимо будет снова изменить Y
и так в цикле. Вы выводите N точек, где N вычисляется из значения https://www.cyberforum.ru/cgi-bin/latex.cgi?L=2\pi R. Так как Вам надо нарисовать 1/8 окружности N=L/8=pR/4≈157*R/200.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
.286
.model tiny
.code
RADIUS EQU 99 ;рисуем окружность с радиусом 99
RADIUS2 EQU RADIUS*RADIUS ;квадрат радиуса
DIAMETR EQU RADIUS*2 ;диаметр окружности
N EQU 157*RADIUS/200;количество точек на 1/8
COLOR EQU 10 ;цвет окружности
ORG 100h
start: MOV AH,0Fh ;узнать номер текущего видеорежима
INT 10h
MOV VIDEOR,AL ;запомним текущий видеорежим
MOV AX,13h;установить видеорежим 320х200х256
INT 10h
PUSH 0A000h;установить регистр ES на сегмент
POP ES ; видеопамяти
XOR BP,BP ;будем увеличивать X и Y
MOV Y,RADIUS-1 ;координаты X=0 и Y=R
CALL DRAW_OCT1 ;рисуем восьмушку окружности
MOV BP,RADIUS-1 ;координата X=2*R
MOV Y,0 ;координата Y=0
CALL DRAW_OCT2 ;рисуем восьмушку окружности
NEG DELTA_X ;увеличиваем Y и уменьшаем X
MOV Y,RADIUS
MOV BP,DIAMETR ;координаты Y=R и X=2*R
CALL DRAW_OCT1 ;рисуем восьмушку окружности
MOV BP,RADIUS ;координата X=R
MOV Y,0 ;координата Y=0
CALL DRAW_OCT2 ;рисуем восьмушку окружности
NEG DELTA_Y ;уменьшаем координаты Y и X
MOV Y,RADIUS ;координата Y=R
MOV BP,DIAMETR ;координата X=2*R
CALL DRAW_OCT1 ;рисуем восьмушку окружности
MOV BP,RADIUS ;координата X=R
MOV Y,DIAMETR ;координата Y=2*R
CALL DRAW_OCT2 ;рисуем восьмушку окружности
NEG DELTA_X ; уменьшаем Y и увеличиваем X
XOR BP,BP ;координата X=0
MOV Y,RADIUS ;координата Y=R
CALL DRAW_OCT1 ;рисуем восьмушку окружности
MOV BP,RADIUS ;координата X=R
MOV Y,DIAMETR ;координата Y=2*R
CALL DRAW_OCT2 ;рисуем восьмушку окружности
XOR AX,AX ;ожидание нажатия любой клавиши
INT 16h
MOV AX,WORD PTR VIDEOR;восстановление видеорежима
INT 10h
RET ;выход из программы
PROC DELTA_CALC ;рассчитаем ошибку накопления
MOV BX,AX ;в AX значение координаты X или Y
DEC AX ;вычислим (Y+0,5)2 » Y2+Y
MUL AX ;или (X+0,5)2 » X2+X
ADD AX,BX
MOV DELTA,AX ;и поместим это значение в DELTA
RET
ENDP
;процедура прорисовки 1/8 окружности с вычислением
PROC DRAW_OCT1 ; координаты X
MOV AX,Y
SHL AX,6 ;должно быть DI=Y*320, но для умножения
MOV DI,AX ;на 320 используем сдвиги, AX= Y*64,
SHL AX,2 ;сохраним AX в DI и умножим Y*64 на 4
ADD DI,AX ;DI=Y*(256+64)=Y*320.
MOV AX,BP
SUB AX,RADIUS ;BP=X AX=R-X
CALL DELTA_CALC ;расчет ошибки накопления по X
MOV CX,N
CIRC1: MOV AX,Y
SUB AX,RADIUS ;AX=Y-R
MUL AX
NEG AX
ADD AX,RADIUS2 ;AX=R2-Y2
CMP DELTA,AX ;сравнить текущий X2=R2-Y2 с ошибкой
JBE A3 ;накопления, если меньше, увеличиваем или
ADD BP,DELTA_X;уменьшаем только Y, иначе
MOV AX,BP;увеличиваем или уменьшаем еще и X и
SUB AX,RADIUS; вычисляем новую ошибку накопления
CALL DELTA_CALC
A3: CMP DELTA_Y,1
JNE A1
ADD DI,320
JMP SHORT A2
A1: SUB DI,320
A2: MOV BYTE PTR ES:[DI][BP],COLOR;выводим точку на
MOV AX,DELTA_Y; экран
ADD Y,AX
LOOP CIRC1 ;повторяем цикл
RET
ENDP
;процедура прорисовки 1/8 окружности с вычислением
PROC DRAW_OCT2 ; координаты X
MOV AX,Y
SHL AX,6 ;должно быть DI=Y*320, но для умножения
MOV DI,AX ;на 320 используем сдвиги, AX= Y*64,
SHL AX,2 ;сохраним AX в DI и умножим Y*64 на 4
ADD DI,AX ;DI=Y*(256+64)=Y*320.
MOV AX, Y
SUB AX,RADIUS
CALL DELTA_CALC
MOV CX,N
CIRC2: MOV AX,BP
SUB AX,RADIUS
MUL AX
NEG AX
ADD AX,RADIUS2 ;AX=R^2-(X-R)^2
CMP DELTA,AX
JBE A5
MOV AX,DELTA_Y
ADD Y,AX
MOV AX,Y
SUB AX,RADIUS
CALL DELTA_CALC
CMP DELTA_Y,1
JNE A4
ADD DI,320
JMP SHORT A5
A4: SUB DI,320
A5: ADD BP,DELTA_X
MOV BYTE PTR ES:[DI][BP],COLOR
LOOP CIRC2
RET
ENDP
VIDEOR DB 0,0 ;значение текущего видеорежима
DELTA DW 0 ;ошибка накопления
DELTA_X DW 1 ;смещение по оси X
DELTA_Y DW 1 ;смещение по оси Y
Y DW 0 ;координата Y
END start
Данная программа выводит на экран окружность, заданного радиуса и цвета, с центром, определенным координатами (R, R). Скорость прорисовки окружности можно увеличить, если не вычислять координату в каждой точке, а вычислять координаты только в начале рисования 1/8 окружности, а далее прибавлять или вычитать 1 к содержимому регистра BP при изменении координаты X и прибавлять или вычитать 320 (длина строки в режиме 13h (320x200x256)) к содержимому регистра DI при изменении координаты Y. Также для увеличения скорости умножение координаты Y на 320 заменено на операции сдвига и сложения.
12
Ушел с форума
Автор FAQ
15840 / 7422 / 994
Регистрация: 11.11.2010
Сообщений: 13,385
30.05.2013, 12:08  [ТС] 8
Вывод монохромного рисунка на экран в режиме от 0Dh до 12h
Программа приведенная ниже будет работать без изменений в следующих графических режимах приведенных в таблице
номер
режима
разрешение
экрана
число
цветов
0Dh320х20016
0Eh640х20016
0Fh640х350моно
10h640х35016
11h640x480моно
12h640x48016
Исходный текст, СОМ-файл и картинка здесь
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
; masm dos com #
.286
.model tiny
.code
.386; используется инструкция MOVSD
org 100h
BITMAPFILEHEADER STRUCT
  bfType        WORD      ?
  bfSize        DWORD      ?
  bfReserved1   WORD      ?
  bfReserved2   WORD      ?
  bfOffBits     DWORD      ?
BITMAPFILEHEADER ENDS
 
BITMAPINFOHEADER STRUCT
  biSize            DWORD      ?
  biWidth           DWORD      ?
  biHeight          DWORD      ?
  biPlanes          WORD       ?
  biBitCount        WORD       ?
  biCompression     DWORD      ?
  biSizeImage       DWORD      ?
  biXPelsPerMeter   DWORD      ?
  biYPelsPerMeter   DWORD      ?
  biClrUsed         DWORD      ?
  biClrImportant    DWORD      ?
BITMAPINFOHEADER ENDS
start:  mov ax,12h;здесь может быть режим от 0Dh до 12h
    int 10h
    push 0A000h
    pop es
    mov ax,3D00h    ;функция открытия, только чтение
    mov dx,offset filename;имя bmp-файла
    int 21h
    mov bx,ax   ;запомнить номер файла  
    mov cx,sizeof BITMAPFILEHEADER   ;длина записи
    mov dx,offset buffer
    mov ah,3Fh      ;функция чтения
    int 21h
    mov di,dx
    assume di: ptr BITMAPFILEHEADER
    cmp [di].bfType,"MB" ;bmp-файл?
    jnz exit
    mov si,word ptr [di].bfOffBits
    add si,dx
    add dx,sizeof BITMAPFILEHEADER
    mov cx,word ptr [di].bfSize
    sub cx,sizeof BITMAPFILEHEADER
    mov ah,3Fh      ;функция чтения
    int 21h
    mov ah,3Eh      ;закрываем файл
    int 21h
    add di,sizeof BITMAPFILEHEADER
    assume di: ptr BITMAPINFOHEADER
    cmp [di].biBitCount,1 ;если файл не монохром
    jnz exit ;завершаем программу
    mov bp,word ptr [di].biWidth
    add bp,15
    and bp,not 15;делаем ширину кратной 16
    shr bp,3;ширину делим на 8, так как в 1 байте 8 точек
    add word ptr [delta+2],bp
        shr bp,2;будем выводить на экран по 4 байта
    mov dx,word ptr [di].biHeight
        imul di,dx,80;(высота рисунка)х(ширина экрана)/8 точек
@@: mov cx,bp
    rep movsd
delta:  sub di,80;(ширина экрана)/8 точек
    dec dx
    jnz @b
    mov ah,0
    int 16h
exit:   mov ax,3
    int 10h
    ret
filename db "coon.bmp"
buffer db 0
end start
Вывод монохромного рисунка на экран в режиме 13h (320х200х256 цветов)
Исходный текст, СОМ-файл и картинка здесь
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
; masm dos com #
.286
.model tiny
.code
org 100h
BITMAPFILEHEADER STRUCT
  bfType        WORD      ?; информация о типе файла
  bfSize        DWORD      ?; размер самого файла в байтах
  bfReserved1   WORD      ?; нули (зарезервировано)
  bfReserved2   WORD      ?; тоже нули
  bfOffBits     DWORD      ?; смещение относительно начала файла до битового массива картинки
BITMAPFILEHEADER ENDS
 
BITMAPINFOHEADER STRUCT
  biSize            DWORD      ?; размер структуры BITMAPINFOHEADER в байтах
  biWidth           DWORD      ?; ширина изображения (в пикселях)
  biHeight          DWORD      ?; высота изображения в пикселях
  biPlanes          WORD       ?; количество плоскостей
  biBitCount        WORD       ?; количество бит на пиксель
  biCompression     DWORD      ?; тип сжатия
  biSizeImage       DWORD      ?; размер изображения в байтах
  biXPelsPerMeter   DWORD      ?; горизонтальное разрешение
  biYPelsPerMeter   DWORD      ?; вертикальное разрешение
  biClrUsed         DWORD      ?; текущее число цветов графического движка
  biClrImportant    DWORD      ?; количество важных цветов
BITMAPINFOHEADER ENDS
start:  mov ax,13h;установили видеорежим
    int 10h
    mov ax,1010h;установить один регистр цвета
    mov bx,0FFh;номер цвета=0FF
    mov dh,3Fh;яркость красного
    mov cx,3F3Fh;яркость зеленого (CH) и синего (CL)
    int 10h
    xor bx,bx;то же самое для цвета=0
    mov dx,bx
    mov cx,bx
    int 10h
    push 0A000h;адрес видеобуфера
    pop es
    mov ax,3D00h    ;функция открытия, только чтение
    mov dx,offset filename;имя bmp-файла
    int 21h
    mov bx,ax   ;запомнить номер файла  
    mov cx,sizeof BITMAPFILEHEADER;длина структуры
    mov dx,offset buffer
    mov ah,3Fh      ;читаем BITMAPFILEHEADER из bmp-файла
    int 21h
    mov di,dx
        assume di: ptr BITMAPFILEHEADER
        cmp [di].bfType,"MB" ;это действительно bmp-файл?
        jnz exit        ;если нет, тогда выходим из программы
    mov si,word ptr [di].bfOffBits;получаем смещение 
    add si,dx;от начала буфера до начала растра bmp-файла
    add dx,sizeof BITMAPFILEHEADER
    mov cx,word ptr [di].bfSize;получаем размер bmp-файла и минусуем из этого размера 
    sub cx,sizeof BITMAPFILEHEADER;структуру BITMAPFILEHEADER, которую уже прочитали
    mov ah,3Fh      ;читаем в буфер остатки bmp-файла
    int 21h
    mov ah,3Eh      ;закрываем файл, он нам уже не нужен
    int 21h
    add di,sizeof BITMAPFILEHEADER
        assume di: ptr BITMAPINFOHEADER
    cmp [di].biBitCount,1 ;если файл не монохром
        jnz exit ;завершаем программу
        mov bp,word ptr [di].biWidth;ширина рисунка
        add bp,31;делаем ее кратной 32. Откуда 32? (biWidth/8)/4 == biWidth/32. 
;Ненулевой остаток от деления говорит о необходимости выравнивания
        and bp,not 31
        add word ptr [delta+2],bp;рассчитываем смещение для вывода строк - пример модификации программного кода
    shr bp,3; запоминаем в регистре ВР ширину рисунка деленную на 8
        mov dx,word ptr [di].biHeight; запоминаем в регистре DX высоту рисунка
        imul di,dx,320;координаты левого нижнего угла рисунка на экране
@@@:    mov cx,bp
@@: lodsb
        mov ah,al
    rept 8;8 раз сдвигаем регистр АН и если есть единичка делаем AL=0FFh иначе AL=0
        add ax,ax
        db 0D6h;salc == sbb al,al
        stosb;выводим содержимое регистра AL на экран
        endm
        loop @b
delta: sub di,320;переходим на строку выше, вместо 320 тут будет другое значение
    dec dx
    jnz @@@
    mov ah,0;ждем пока не нажмут любую клавишу
    int 16h
exit:   mov ax,3;устанавливаем текстовый режим
    int 10h
    ret;выходим из программы
filename db "coon.bmp"
buffer   db 0; последняя метка в СОМ-программе, после нее можно организовать буфер размером до 63 кбайт
end start
Вывод монохромного рисунка на экран в VESA-режиме 640x480x256
Исходный текст, СОМ-файл и картинка здесь
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
; masm dos com #
.286
.model tiny
.code
org 100h
BITMAPFILEHEADER STRUCT
  bfType        WORD      ?
  bfSize        DWORD      ?
  bfReserved1   WORD      ?
  bfReserved2   WORD      ?
  bfOffBits     DWORD      ?
BITMAPFILEHEADER ENDS
 
BITMAPINFOHEADER STRUCT
  biSize            DWORD      ?
  biWidth           DWORD      ?
  biHeight          DWORD      ?
  biPlanes          WORD       ?
  biBitCount        WORD       ?
  biCompression     DWORD      ?
  biSizeImage       DWORD      ?
  biXPelsPerMeter   DWORD      ?
  biYPelsPerMeter   DWORD      ?
  biClrUsed         DWORD      ?
  biClrImportant    DWORD      ?
BITMAPINFOHEADER ENDS
start:  mov ax,4F02h
        mov bx,101h;установка режима 640*480*256
    int 10h
    mov ax,1010h;установить один регистр цвета
    mov bx,0FFh;номер цвета
    mov dh,3Fh;яркость красного
    mov cx,3F3Fh;яркость зеленого (CH) и синего (CL)
    int 10h
    xor bx,bx
    mov dx,bx
    mov cx,bx
    int 10h
    push 0A000h
    pop es
    mov ax,3D00h    ;функция открытия, только чтение
    mov dx,offset filename;имя bmp-файла
    int 21h
    mov bx,ax   ;запомнить номер файла  
    mov cx,sizeof BITMAPFILEHEADER   ;длина записи
    mov dx,offset buffer
    mov ah,3Fh      ;функция чтения
    int 21h
    mov di,dx
        assume di: ptr BITMAPFILEHEADER
        cmp [di].bfType,"MB" ;bmp-файл?
        jnz exit
    mov si,word ptr [di].bfOffBits
    add si,dx;offset buffer
    add dx,sizeof BITMAPFILEHEADER
    mov cx,word ptr [di].bfSize
    sub cx,sizeof BITMAPFILEHEADER
    mov ah,3Fh      ;функция чтения
    int 21h
    mov ah,3Eh      ;закрываем файл
    int 21h
    add di,sizeof BITMAPFILEHEADER
        assume di: ptr BITMAPINFOHEADER
    cmp [di].biBitCount,1 ;если файл не монохром
        jnz exit ;завершаем программу
        mov bp,word ptr [di].biWidth;берем ширину картинки
        add bp,31
        and bp,not 31;делаем ширину кратной 32
        add word ptr delta0+2,bp;изменяем смещение для перехода на новую строку
    shr bp,3;делим ширину на 8, так как в байте BMP информация о 8 пикселах
        mov ax,word ptr [di].biHeight;делим высоту картинки
    mov bx,0FFFFh/640;на высоту окна видеопамяти 
    xor dx,dx;чтобы знать с какого окна видеопамяти начинать вывод
    div bx
    mov word ptr delta1+1,dx;изменяем количество циклов для последнего окна
    xor bx,bx;выбор окна видеопамяти
    mov dx,ax;адрес окна видеопамяти в единицах гранулярности (от 0 до 4)
    inc ax
    imul di,ax,-256;установка начального смещения di
    cmp di,-640    ;dx=0 di=-256, dx=1 di=-512, dx=2 di=-128
    sbb ax,ax      ;dx=3 di=-384, dx=4 di=-640 
    and ax,640
    add di,ax
@3: mov cx,0FFFFh/640 
@2: mov ax,4F05h;управление доступом к видеопамяти
        int 10h
@1: push cx
    mov cx,bp
@@: lodsb
        mov ah,al
    rept 8   ;8 раз извлекаем по 1 биту и если бит равен 1 - делаем AL=0FF,
        add ax,ax;а если бит равен 0 - делаем AL=0
        db 0D6h  ;sbb al,al
        stosb    ;пишем в видеопамять
        endm
        loop @b
delta0: sub di,640;перемещеаемся на строку вверх
    pop cx
    loop @1
    dec dx
    jg @3; если dx > 0  
delta1: mov cx,0FFFFh
    jns @2; если dx < 0  выходим из цикла
    mov ah,0; ожидаем нажатия на любую клавишу
    int 16h
exit:   mov ax,3;восстанавливаем текстовый режим
    int 10h
    ret     ;выходим из программы
filename db "coon.bmp";имя ВМР-файла
buffer db 0;начало буфера и одновременно терминирующий символ для имени файла
end start
3
Ушел с форума
Автор FAQ
15840 / 7422 / 994
Регистрация: 11.11.2010
Сообщений: 13,385
30.05.2013, 12:15  [ТС] 9
описание ключей ml.exe
ml.exe - это транслятор языка Macro Assembler фирмы MicroSoft.
Главная задача этой программы: из исходника перевести все команды Ассемблера, набранные текстом в коды машинных команд.
Сам ml.exe не создает готовую программу под конкретную операционную систему и её формат. Он делает промежуточный, так называемый объектный файл (с расширением obj). Но после ml.exe вызваем линковщик. Так мы получим готовую программу.
Как и большинство трансляторов разных языков программирования, ml.exe - это программа с интерфейсом командной строки.
Здесь мышка не поможет, нет смысла пытаться "открывать" компилятор из проводника без указания в командной строке хотя бы файла исходника.
Вид строки:
Код
ml [ключи] список_файлов [/link <ключи_линковщика>]
Где: ключи_линковщика - параметры командной строки, которые будут переданы линковщику.
/link - параметр, с которым указываются ключи_линковщика (см. таблицы ключей).
список_файлов - имена исходных файлов, которые будут транслироваться.
ключи - параметры компиляции, перечисленные ниже (ключи чувствительны к регистру).
Параметры, которые вы используете постоянно, можно добавить в переменную окружения ML. Транслятор будет учитывать эти опции всегда.
Форматы трансляции
Главная характеристика трансляции - это тип выходного объектного файла.
ML.EXE умеет создавать только два вида obj-файлов.
  • MS COFF - объектный формат, который по устройству очень близок к PE-формату. MS COFF-формат используется для компиляции программ под Win32.
  • Intel OMF - объектный формат, который в основном используется для создания exe-модулей под DOS.
  • Более ранние версии транслятора MASM умеют создавать ещё и Tiny model, она используется для создания com-программ. Формат подходит только для небольших DOS-программ, умещающихся в 64kb вместе c данными и стеком.
  1. Ключи формата трансляции
    Ключ и форматЧто делаетДля чего это нужноПримечаниеприменение для win32
    /coffВключает MS COFF-формат (MS Common Object File Format)Для создания обычных Win32-программС 7-й версии ml.exe этот ключ можно не указывать, так как COFF-формат сделали форматом "по-умолчанию" 
    /omfВключает объектный формат Intel OMF (Intel Object Module Format)Omf-формат используется для компиляции DOS-программ.Этот ключ появился только в 7-й версии. Раньше OMF был форматом "по-умолчанию" 
    /ZsТранслятор только проверяет синтаксис исходникаML.EXE просто выдает список ошибок. Даже в том случае если ошибок нет, сама трансляция не состоится Обычно не применяется. Подавляет формирование объектного модуля.
    Свойства будущей программы

    Дополнительно можно задать некоторые характеристики для выходного объектного и затем исполняемого файла.
  2. Ключи свойств будущей программы
    Ключ и форматЧто делаетДля чего это нужноПримечание
    /F <hex-число>Устанавливает резерв стека. Размер указывается после пробела в байтах в шестнадцатиричном виде. Для Win32-программ это число лучше сделать кратным 1000hПо умолчанию для стека резервируется 4kb (1000h). Но Windows динамически определяет необходимый объём стека 32-битных приложений. Для оптимизации имеет смысл задать размер стека, который всё равно будет использоваться программой. Тогда отпадает необходимость часто увеличивать стек динамическим путёмВместо этого ключа можно использовать ключ "/STACK" для link.exe
    /FPiДобавляет в программу код эмулятора команд математического сопроцессора 80x87Старые процессоры (до 80486DX) не имели встроенного блока сопроцессора, поэтому для того, чтобы программу с FPU-инструкциями можно было запустить на древних компьютерах без сопроцессора (например 486SX), была создана специальная библиотека с кодом, эмулирующим инструкции с плавающей запятойСегодня этот ключ практически не используется
    /G<c l d l z>Определяет соглашение о вызове функций: с - Pascal d - С z - StdcallЭтот ключ определяет множество параметров вызова функций (вид имен функций, порядок аргументов, выравнивание стека). В программах Win32 всегда используется соглашение Stdcall. Почти все Win32-API сами выравнивают стек. Исключением является лишь API wsprintf, она определена отдельно с соглашением CВместо этого ключа можно использовать директиву ".model" или "OPTION LANGUAGE:" в исходном файле
    /H<число>Устанавливает максимальную длину внешних имён (число символов в десятичном виде)В Win32-библиотеках имена экспортируемых (внешних) функций могут быть очень длинными, и ограничивать их нет смысла. Возможное использование ключа - разве что для совместимости со старыми программами и системами 
    /safesehПомечает объектный файл как не содержащий SEH (структурных обработчиков исключений) или содержащий только SEH'и объявленные с директивой .SAFESEH. Такая возможность появилась только в 7-й версии ml.exeПозволяет создавать объектные файлы с использованием безопасных обработчиков структурных исключений (safe SEH). Методика Safe SEH разработана MS для предотвращения использования обработчиков исключений (SEH) вредоносным кодом. Этот ключ нет смысла использовать в составе с MASM32 (до текущего v9). Чтобы из таких obj-файлов создавать исполняемые модули с safe SEH, нужно использовать link.exe версии 7 и старше с ключом /SAFESEH. Кроме того, нужны соответствующие lib-файлы. Так что для полезного использования этого ключа придётся как минимум подключать к исходнику новые lib-файлы из Visual С++.Если ваш исходник вообще не содержит SEH, то после компиляции PE-exe или dll достаточно включить бит 10 в DLL-флагах (MAGE_DLLCHARACTERISTICS_NO_SEH = 400h), чтобы модуль считался Safe SEH.
    /Zp[число]Устанавливает выравнивание внутри всех структур (число может быть 1,2,4 и 8)Позволяет автоматически выравнивать все члены относительно первого внутри всех структур до границы в 1,2,4 или 8 байт. Положение первых членов не выравнивается.Вместо этого ключа в исходнике можно использовать поле [alignment] в директиве STRUCT для каждой структуры отдельно
    Ключи трактовки исходного кода
    Можно заставить в ходе трансляции "доработать" исходник. Изменить идентификаторы (имена меток, переменных...) или добавить макросы. При этом исходный файл на диске меняться не будет.
  3. Ключи модификации исходного текста
    Ключ и форматЧто делаетДля чего это нужно Примечаниеприменение для win32
    /CpОставляет неизменным регистр для всех пользовательских идентификаторовЭтот ключ позволяет считать все имена типа Тыква, ТЫКВА и тыква разными. В исходниках для Win32 такая возможность используется всегдаВместо этого ключа удобнее использовать директиву "option casemap:none" в исходном файле. Ключи не отменяют директивы, поэтому /Cp ничего не изменит, если в исходнике есть "option casemap:all или notpublic"Применение не обязательно, но возможно для дополнительного контроля синтаксиса. Вызывает ошибку "A2006: undefined symbol" при несовпадении регистра в объявлении идентификатора и обращении к нему. Позволяет избежать ошибок на этапе компоновки в случае, если идентификатор объявлен с неверным регистром.
    /CuПоднимает регистр для всех пользовательских идентификаторовФактически, этот ключ ничего не даёт. Так как и без него все имена типа Тыква, ТЫКВА и тыква приводятся к виду ТЫКВАВместо этого ключа можно использовать директиву "option casemap:all" в исходном файлеНе применяется, так как компоновка приложений Win32 чувствительна к регистру.
    /CxОставляет неизменным регистр только для идентификаторов, объявленных публичными или внешнимиЭтот ключ позволяет оставить для линковщика внешние и публичные имена типа Тыква, ТЫКВА и тыква как они есть. Сейчас этот ключ практически не используетсяВместо этого ключа можно использовать директиву "option casemap:notpublic" в исходном файлеВ применении нет необходимости. Регистр идентификаторов имеет смысл на этапе компоновки, но не на этапе компиляции.
    /D<имя>[=значение]Определяет макрос в командной строке. Имя - идентификатор, значение - код макроса (если с пробелами, то должен быть в кавычках)При помощи этого ключа можно добавлять к исходнику текстовые макросы в командной строкеМакросы могут быть такими же, как с директивой "EQU" или "TEXTEQU" в исходном файле. Описание синтаксиса смотри в help-файлах к MASM32Применяется по усмотрению программиста. Аналог директив EQU или =. Если текст содержит пробелы, его следует взять в кавычки. Обычно используется в отладочном версии приложения для объявления имени DEBUG.
    /ZmВключает режим максимальной совместимости с MASM 5.10Для компиляции очень старых исходников, написанных под MASM до версии 5.10Вместо этого ключа можно использовать директиву "option M510" в исходном файлеОбычно не применяется. Отключает полезные для прикладного программирования свойства MASM, введенные в версиях 6.1+.
  4. Ключи для работы с именами файлов и путями
    Ключ и форматЧто делаетДля чего это нужноприменение для win32
    /Fe<файл>Задает альтернативное имя для исполняемого файлаМожно оставлять старые версии готовой программы нетронутыми и давать им каждый раз новые номера. Этот ключ удобно использовать в bat-файлах для автоматизации бекаповНе применяется, так как с учетом опции /c компилятор не создает исполняемого файл
    /Fo<файл>Задает альтернативное имя для объектного файлаЕсли мы хотим, чтоб промежуточный объектный файл имел имя, отличное от исходника, то нужно указать желаемое имя в поле <файл>Обычно не применяется. Позволяет задать obj-файлу имя, отличное от имени asm-файла.
    /I<путь>Задает пути для включаемых файловДопустим, в исходнике такая строка: include windows.inc Транслятор будет искать windows.inc в текущей папке, а затем в путях, указанных в переменных окружения PATH и INCLUDE. Но с данным ключом ещё и в <пути> (доступно до 10 штук /I). Слишком часто в исходниках конкретизируют путь так: include \masm32\include\windows.inc При такой записи файл windows.inc будет искаться только на текущем диске с этим путём и ключ "/I", к сожалению, работать не будетНе применяется, так как собственных возможностей MS Developer Studio обычно достаточно для определения путей к inc-файлам. Допускается использовать до 10 опций /I.
    /XИгнорирует переменную окружения INCLUDEДанный ключ используется для того, чтобы не искать inc-файлы MASM'a там, где находятся сторонние подключаемые файлы. Транслятор без ключей /I и /X ищет включаемые файлы по указанному в исходнике пути. Если он не установлен: в текущей папке и затем в двух переменных окружения: PATH и INCLUDE. Последнюю определяет под себя не только транслятор ml.exe, но и другие компиляторы (например C++)Обычно не применяется, так как при работе в среде MS Developer Studio переменная окружения INCLUDE не используется.
    /Ta<файл>Задает имя исходника с расширением, отличным от asmНазначение для меня неясно, потому что сегодня можно компилировать исходник с любым расширением и без этого ключаОбычно не применяется. Служит для компиляции файлов, имя которых имеет расширение, отличное от .asm.
    Листинг
    ML.EXE может выдавать текстовые файлы с информацией о ходе трансляции, это и называется листингом.
    Файлы листинга можно использовать в разных целях, например для:
    • отладки программ
    • оптимизации кода
    • обучения
  5. Ключи управления листингом
    Ключ и форматЧто делаетДля чего это нужноприменение для win32
    /EPОтправляет листинг препроцессора на поток вывода stdout (консольный вывод)С этим ключом мы получим листинг первого прохода - текст исходного файла и всех подключенных к нему после обработки препроцессором. Текст выводится в окно консоли. Если перенаправить вывод в файл, то мы получим самостоятельный исходник (не нуждающийся в доп. inc-файлах), однако такой файл уже не будет содержать исходных макросов и, как правило, он получается больше мегабайта. Бывает полезно для отладки макросов и при переносе на другую машину (с другой версией MASM'a) или для "глубокого" архиваОбычно применять нет необходимости. Листинг препроцессора представляет собой исходный текст вместе с включаемыми файлами.
    /Fl[файл]Создает файл листинга трансляцииСоздается текстовый файл, который помогает понять, во что транслируется каждая строка исходников. Чтобы найти свой текст в листинге транслятора (отчет обычно больше 3Mb), лучше сразу искать свои комментарииОбычно не применяется, так как средства MS Developer Studio, как правило, достаточны для работы с текстом приложения.
  6. ключи для настройки листинга трансляции
    Ключ и форматЧто делаетприменение для win32
    /SaСамый полный формат листинга. Задает для листинга все опции форматаПрименяется редко, так как собственных средств MS Developer Studio обычно достаточно для работы с исходным и компилированным текстом программы, и в выдаче листинга нет необходимости.
    /SfДобавляет в листинг первичный проход транслятора 
    /ScДобавляет в листинг перед каждой машинной командой количество тактов, за которое она выполняется (информация на основе директив ряда ...".386",".486"... из исходника) 
    /SgФактически, на сегодня этот ключ ничего не меняет 
    /Sl<ширина>Устанавливает длину строки листинга от 60 до 255 символов (по умолчанию значение 0 - не ограниченно) 
    /Sp<высота>Устанавливает количество строк в условной странице листинга от 10 до 255 (по умолчанию значение 0 - не ограниченно). На таблицу символов не распространяется 
    /St<текст>Добавляет в листинг заголовок 
    /Ss<текст>Добавляет в листинг подзаголовки 
    /SxВключает в листинг не выполненные фрагменты условий 
    /SnИсключает из листинга таблицу символов 
  7. ключи управления сопроводительными файлами
    Ключ и форматЧто делаетДля чего это нужноПримечаниеприменение для win32
    /Fm[файл]Просит линковщик создать map-файлФактически этот текстовый файл является отчётом линковщика. В map-файлах указываются данные о секциях, импорте, экспорте и т.п.Вместо этого ключа можно использовать ключ "/MAP[:filename]" для link.exeНе применяется, так как map-файл создается компоновщиком, а с учетом опции /c компилятор не вызывает компоновщик
    /FR[файл]Создает sbr-файл для браузера объектов с расширенной информациейИмеет смысл использовать с Visual Studio. Sbr - это промежуточные файлы, которые затем можно преобразовать (с помощью bscmake.exe) в bsc-файлы проводника объектов VS. Позволяет получать быстрый доступ к любому идентификатору во всем пространстве проекта и заголовочных файлов API win32.
    /Fr[файл]Создает sbr-файл для браузера объектов с ограниченной информациейНазначение смотри выше. Ограниченная - это значит не включается информация о локальных идентификаторах (локальные метки, переменные и т.п.) Применение менее предпочтительно, чем /FR, так как в информацию браузера не включаются сведения о локальных идентификаторах.
    Ошибки и предупреждения
    Каждая ошибка трансляции имеет свой номер - код ошибки. Коды распределяются так:
    • A1??? - смертельные ошибки
    • A2??? - несмертельные ошибки
    • A3000-A6??? - предупреждения
  8. Ключи свойств, ошибок и предупреждений
    Ключ и форматЧто делаетДля чего это нужноприменение для win32
    /ATВключает набор сообщений об ошибках для com-программ (tiny model). Tiny model не поддерживается с 7-й версии ml.exe, но ключ осталсяЭтот ключ - не эквивалент директивы ".MODEL tiny" в исходном файле, однако с ним ml.exe передаст линковщику опцию /T (/TINY). Ключ /TINY не совместим с Incremental Linker. Он просто вызовет ошибку линковкиНе применяется, так как формат исполняемого файла .com не используется в Win32.
    /WXРассматривает предупреждения транслятора как ошибкиЕсли возникнут предупреждения, то с этим ключом трансляция прекращается после вывода списка ошибокОбычно в применении нет необходимости. В случае возникновения предупреждений компиляция завершается неуспешно.
    /W<число>Устанавливает уровень предупреждений (число может быть 0,1,2 или 3)Ключ: Показывает ошибки: /W0 - A1000-A2??? /W1 - A1000-A4??? /W2 - A1000-A5??? /W3 - A1000-A6???Обычно в применении нет необходимости. Устанавливает перечень событий компиляции, трактуемых как предупреждения.
    /wравен /W0  
    Отладочная информация
    ML.EXE может поместить в объектный файл имена наших переменных, функций и другие идентификаторы пользователя. Кроме того, линковщик может связать каждую строку исходника с участком маш. кода, в который она была транслирована. Всё это называется отладочной информацией.
  9. Ключи управления отладочной информацией
    Ключ и форматЧто делаетДля чего это нужноприменение для win32
    /ZiСоздает полную отладочную информациюИспользуется на этапе отладки программы. Транслятор включает в объектный файл полную отладочную информацию, которую линковщик (только с ключом /DEBUG) формирует в отладочный в pdb-файл (program database) и связывает с исполняемым модулем. С таким файлом в отладчике можно будет видеть исходник и всю информацию типа имен функций, переменных, меток и так далееОбязательно применяется на этапе отладки. Формат отладочной информации MASM полностью совместим с используемым встроенным отладчиком MS Developer Studio.
    /ZfСделает только идентификаторы публичнымиМожно использовать для обеспечения сторонних программистов отладочной информацией (например для SDK). Транслятор включает в объектный файл отладочную информацию об идентификаторах, которую линковщик (только с ключом /DEBUG) формирует в отладочный pdb-файл и связывает с исполняемым модулем. С таким pdb-файлом в отладчике можно будет видеть информацию типа имен функций, переменных, меток, но не исходникОбычно не применяется.
    /ZdВ отладочной информации будут только номера строк исходникаРаньше использовался для совместимости со старыми отладчиками, сегодня уже не имеет смыслаОбычно не применяется, так как на этапе отладки более целесообразно использовать опцию /Zi.
  10. ключи управляющие линковкой
    Ключ и форматЧто делаетДля чего это нужноприменение для win32
    /Bl<линковщик>Для сборки программы будет использоваться альтернативный линковщик по указанному путиРазные версии линковщиков обладают разными возможностями (новые могут не поддерживать старые форматы). Для расширения возможностей этот ключ позволяет использовать линковщики с разными именами. Если используется /Bl, то ключи линковщику будут передаваться из поля ключи_линковщикаОбычно не применяется, так как возможностей link.exe вполне достаточно. Используется опция /c
    /link <ключи_линковщика>Задает командную строку линковщикаЧерез этот параметр транслятор передает любые ключи, операторы и имена файлов линковщику. Используется для того, чтобы управлять альтернативным линковщиком 
    /cТранслирует без линковкиЭтот ключ используется для создания только объектных файлов. Часто нужно проводить линковку отдельно.Обязательно для применения в среде MS Developer Studio, чтобы выполнять компоновку отдельным этапом.
  11. Информационные ключи ML
    Ключ и форматЧто делаетДля чего это нужноПримечаниеприменение для win32
    /nologoОтключает вывод текста приветствия Удобно использовать прежде всего с IDE (интегрированными средами разработки)Если вы используете только одну версию ml.exe, то этот ключ лучше всего добавить в переменную окружения MLКак правило, следует применять, так как баннерный текст смысловой нагрузки при разработке проекта не несет.
    /helpВыводит список ключей с кратким пояснениемЧтобы напоминать себе о том, как много всего ключей Из-за ошибки (связанной с UNICODE) многие версии ml.exe не принимают стандартного ключа /?. Альтернативой может быть либо /help, либо -? 
4
1127 / 261 / 9
Регистрация: 11.06.2010
Сообщений: 1,049
30.05.2013, 15:57 10
FPU. Округление.

Порой бывают такие случаи, когда надо вытащить целую часть дроби. В этом случаи все прибегают к помощи такой связки, как
Assembler
1
2
3
finit
fld __float
fistp __int
, но она почему-то не всегда срабатывает.

Например, у меня был случай, когда при делении надо было от частного оставлять только целую часть, но связка, упомянутая выше, в случае с частным 0.501 выдавала результатом 1, а не 0.

Когда такое происходит, помогает вот такая связка:
Assembler
1
2
3
fstcw cw
or cw,0C00h
fldcw cw
Связка выше устанавливает принудительное округление в режиме "Round toward zero" или "Округление к нулю".

После применения данной инструкции всё заработало так, как надо. Вот так.
4
608 / 406 / 8
Регистрация: 26.04.2012
Сообщений: 2,065
31.05.2013, 07:34 11
процедура пищания динамиком под досом.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
;на вход процедуре в регистре di подается частота звучания
sound proc 
    push ax
    push cx
    mov al, 0B6h
    out 43h, al
    in al, 61h
    or al, 3
    out 61h, al
    mov ax, 34DCh
    mov dx, 12h
    div di
    out 42h, al
    mov al, ah
    out 42h, al
    mov cx, 3000        ;небольшая задержка
@@: push cx
    mov cx, 50000
    loop $
    pop cx 
    loop @@
    in al, 61h
    and al, 0FCh
    out 61h, al
    pop cx
    pop ax 
    ret
sound endp
2
608 / 406 / 8
Регистрация: 26.04.2012
Сообщений: 2,065
03.06.2013, 08:35 12
Длинные числа
Стандартные типы данных позволяют хранить не очень длинные числа. в регистре еах максимум может поместиться число 4294967295. Для некоторых целей оно очень мало. Чтобы оперировать числами вида 10^Nпри N>40 нужно изобретать свой тип данных. Один из вариантов – массив.
Предположим, необходимо вычислить 30!:
30!=2*(104)8+6525*(104)7+2748*(104)6+8121*(104)5+9105*(104)4+8636*(104)3+3084*(104)2+8000*(104)1+0*(104)0
Номер элемента в массиве A0 123456789
Значение9080003084863691058121285965252
Значения в массиве хранятся задом наперед, первый элемент массива является счетчиком задействованных под число элементов. ВАЖНО: рассматриваются только положительные числа.

ВВОД ЧИСЛА
Опишем данные:
Pascal
1
2
3
Const MaxDig=1000;{*Максимальное количество цифр — четырехзначных.*}
Osn=10000;{*Основание нашей системы счисления, в элементах массива храним четырехзначные числа.*}
Type TLong=Array[0..MaxDig] Of Integer;{* Вычислите максимальное количество десятичных цифр в нашем числе.*}
Assembler
1
2
3
MaxCount = 10000
Osn=10000
A dd MaxCount dup (0)
Прежде чем рассмотреть процедуру ввода, приведу пример. Пусть вводится число 23851674 и основанием (Osn) является 1000 (храним по три цифры в элементе массива). Изменение значений элементов массива А в процессе ввода (посимвольного — переменная сh) отражено в таблице.
A[0]A[1]A[2]A[3]chПояснение
367485123-Конечное состояние
00002Начальное состояние
120031 шаг
1230082 шаг
12380053 шаг
23852014 шаг
285123065 шаг
2516238076 шаг
3167385247 шаг
367485123  
При обработке каждой очередной цифры входного числа старшая цифра в элементе A[i] становится младшей цифрой в элементе A[i+1], а вводимая цифра будет младшей в элементе A[1].
Алгоритм протаскивания старшей цифры из A[i] в младшую цифру A[i+1]:
Pascal
1
2
3
4
for i:=A[0] downto 1 do begin
             A[i+1]:=A[i+1]+(LongInt(A[i])*10) div Osn;
    A[i]:=(LongInt(A[i])*10) mod Osn;
end;
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@@1:                        
    push ecx                
    mov ecx, [edi]      
    cmp ecx, 1              
    jb @@3
    
@@2:    
    mov eax, [edi+ecx*4]        
    mov ebx, 10
    mul ebx
    mov ebx, Osn
    div ebx
    add [edi+ecx*4+4], eax
    mov [edi+ecx*4], edx
    loop @@2
Пусть мы вводим число 23851674 и первые 6 цифр уже разместили задом наперед в массиве А. В cимвольную переменную ch считали очередную цифру многоразрядного числа — это 7. По нашему алгоритму эта цифра 7 должна быть размещена младшей цифрой в А[1]. Выписанный фрагмент программы освобождает место для этой цифры. В таблице отражены результаты работы этого фрагмента.
iA[1]A[2]A[3]ch
251623807
25163802 
11603852 
После этого остается прибавить 7 к A[1] и изменить значение A[0].
Готовая процедура:
Pascal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
procedure ReadLong(var A:TLong);
var
     s: String;
     i, j: Integer;
begin
     ReadLn(s);
     FillChar(A, Sizeof(A), 0);
     A[0]:=0;
 
     for j:=1 to length(s) do begin
    for i:=A[0] downto 1 do begin
             A[i+1]:=A[i+1]+(LongInt(A[i])*10) div Osn;
               A[i]:=(LongInt(A[i])*10) mod Osn;
         end;
         A[1]:=A[1]+ord(s[j])-48;
         if A[A[0]+1]>0 then Inc(A[0]);
      end;
end
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
ReadLong proc   
    push ebp
    mov ebp, esp
    pushad
    mov esi, [ebp+12]       ;буфер для ввода строки
    push esi
    push offset fmts
    call crt_scanf          ;вводим строку цифр
    add esp, 8
    push [ebp+12]           
    call crt_strlen
    add esp, 4
    mov ecx, eax
    mov edi, [ebp+8]        ;адрес массива для хранения  числа
    mov dword ptr [edi], 0  ;первый элемент массива будет хранит кол-во элементов массива
                            ;в которых лежат числа
;сия конструкция протаскивает старшую цифру числа в конец.                          
@@1:                        
    push ecx                
    mov ecx, [edi]      
    cmp ecx, 1              
    jb @@3
    
@@2:    
    mov eax, [edi+ecx*4]        
    mov ebx, 10
    mul ebx
    mov ebx, Osn
    div ebx
    add [edi+ecx*4+4], eax
    mov [edi+ecx*4], edx
    loop @@2
@@3:
    xor eax, eax                ;прибавляем очередную цифру
    mov al, [esi]
    sub al, '0'
    add [edi+4], eax
    mov ecx, [edi]
    xor ebx, ebx
    cmp dword ptr [edi+ecx*4+4], 0  ;увеличим кол-во использованых элементов массива, если был задействован еще один
    setne bl
    add [edi], ebx  
    inc esi
    pop ecx
    loop @@1
    popad
    mov esp, ebp
    pop ebp
    ret 8
ReadLong endp
ВЫВОД ДЛИННЫХ ЧИСЕЛ
Казалось бы, нет проблем — выводи число за числом. Однако в силу выбранного нами представления числа необходимо всегда помнить, что в каждом элементе массива хранится не последовательность цифр числа, а значение числа, записанного этими цифрами. Пусть в элементах массива хранятся четырехзначные числа. И есть число, например, 128400583274. При выводе нам необходимо вывести не 58, а 0058, иначе будет потеря цифр. Итак, нули также необходимо выводить. Процедура вывода имеет вид:
Pascal
1
2
3
4
5
6
7
8
9
10
11
12
13
procedure WriteLong(const A:TLong);
var s, ls:String;
    i:Integer;
begin
    Str(Osn div 10, ls);
    write(A[A[0]]);
    for i:=A[0]-1 downto 1 do begin
        Str(A[i], s);
        while length(s) < length(ls) do s:='0'+s;
        write(s);
    end;
    writeln;
end;
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
 
WriteLong proc 
    push ebp
    mov ebp, esp
    pushad
    mov edi, [ebp+8]
    mov ecx, [edi]
    push ecx
    push [edi+ecx*4]
    push offset fmti
    call crt_printf         ;выводим первые цифры числа
    add esp, 8
    pop ecx
@@1:
    dec ecx
    cmp ecx, 1
    jl @@4
    push ecx                
    push offset buf
    push [edi+ecx*4]    ;берем очередной элемент массива
    call IntToStr       ;преобразуем в строку    
@@2:          
    cmp eax, count ;если кол-во цифр в строке меньше, чем должно быть, то выводим нули
    je @@3   
    push eax
    push '0'
    call crt_putchar
    add esp, 4   
    pop eax
    inc eax
    jmp short @@2
@@3:
    push offset buf         ;вывод строки с цифрами
    push offset fmts
    call crt_printf
    add esp, 8
    pop ecx
    jmp short @@1
@@4:    
    popad
    mov esp, ebp
    pop ebp
    ret
WriteLong endp
 
;сия процедура есть в прикрепленных темах. отличие лишь в том, что цифры не выводятся на экран, а сохраняются 
;в буфер для последующего вывода.
IntToStr:
    push ebp
    mov ebp, esp    
    push ebx
    push ecx
    push edi
    push edx
    mov edi, [ebp+12]
    mov eax, [ebp+8]
    xor ecx, ecx
    mov ebx, 10
@@1:
    xor edx, edx
    div ebx
    push dx
    inc ecx
    or eax, eax
    jne @@1
    
    mov ebx, ecx
@@2:    
    pop ax
    add al, 30h
    stosb
    loop @@2
    
    xor ax,ax 
    stosb
    mov eax, ebx
    pop edx
    pop edi
    pop ecx
    pop ebx
    mov esp, ebp
    pop ebp
    ret 8
Готовая программа на ассемблере, которая сначала вводит число, а затем его выводит.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
.386
    .model flat,stdcall
    option casemap:none
    include \masm\include\windows.inc
    include \masm\include\kernel32.inc
    include \masm\include\msvcrt.inc
    includelib \masm\lib\msvcrt.lib
    includelib \masm\lib\kernel32.lib
 
.data
    MaxCount = 10000
    Osn=10000
    count=4
    buf db MaxCount*3 dup (0)
    fmts db '%s', 0
    fmti db '%i', 0
    NewLine db 13, 10, 0
    msg1 db 'Enter long number: ', 0
    msg2 db 13, 10, 'You entered: ', 0
    A dd MaxCount dup (0)   
.code
start:  
    push 0
    push offset msg1
    push offset fmts
    call crt_printf
    add esp, 8
    push offset buf
    push offset A
    call ReadLong
    push offset msg2
    push offset fmts
    call crt_printf
    add esp, 8
    push offset buf
    push offset A
    call WriteLong
    call crt_getchar
    call crt_getchar
    call ExitProcess
 
ReadLong proc   
    push ebp
    mov ebp, esp
    pushad
    mov esi, [ebp+12]       ;буфер для ввода строки
    push esi
    push offset fmts
    call crt_scanf          ;вводим строку цифр
    add esp, 8
    push [ebp+12]           
    call crt_strlen
    add esp, 4
    mov ecx, eax
    mov edi, [ebp+8]        ;адрес массива для хранения  числа
    mov dword ptr [edi], 0  ;первый элемент массива будет хранит кол-во элементов массива
                            ;в которых лежат числа
;сия конструкция протаскивает старшую цифру числа в конец.                          
@@1:                        
    push ecx                
    mov ecx, [edi]      
    cmp ecx, 1              
    jb @@3
    
@@2:    
    mov eax, [edi+ecx*4]        
    mov ebx, 10
    mul ebx
    mov ebx, Osn
    div ebx
    add [edi+ecx*4+4], eax
    mov [edi+ecx*4], edx
    loop @@2
@@3:
    xor eax, eax                ;прибавляем очередную цифру
    mov al, [esi]
    sub al, '0'
    add [edi+4], eax
    mov ecx, [edi]
    xor ebx, ebx
    cmp dword ptr [edi+ecx*4+4], 0  ;увеличим кол-во использованых элементов массива, если был задействован еще один
    setne bl
    add [edi], ebx  
    inc esi
    pop ecx
    loop @@1
    popad
    mov esp, ebp
    pop ebp
    ret 8
ReadLong endp
 
WriteLong proc 
    push ebp
    mov ebp, esp
    pushad
    mov edi, [ebp+8]
    mov ecx, [edi]
    push ecx
    push [edi+ecx*4]
    push offset fmti
    call crt_printf         ;выводим первые цифры числа
    add esp, 8
    pop ecx
@@1:
    dec ecx
    cmp ecx, 1
    jl @@4
    push ecx                
    push offset buf
    push [edi+ecx*4]    ;берем очередной элемент массива
    call IntToStr       ;преобразуем в строку    
@@2:          
    cmp eax, count ;если кол-во цифр в строке меньше, чем должно быть, то выводим нули
    je @@3   
    push eax
    push '0'
    call crt_putchar
    add esp, 4   
    pop eax
    inc eax
    jmp short @@2
@@3:
    push offset buf         ;вывод строки с цифрами
    push offset fmts
    call crt_printf
    add esp, 8
    pop ecx
    jmp short @@1
@@4:    
    popad
    mov esp, ebp
    pop ebp
    ret
WriteLong endp
 
;сия процедура есть в прикрепленных темах. отличие лишь в том, что цифры не выводятся на экран, а сохраняются 
;в буфер для последующего вывода.
IntToStr:
    push ebp
    mov ebp, esp    
    push ebx
    push ecx
    push edi
    push edx
    mov edi, [ebp+12]
    mov eax, [ebp+8]
    xor ecx, ecx
    mov ebx, 10
@@1:
    xor edx, edx
    div ebx
    push dx
    inc ecx
    or eax, eax
    jne @@1
    
    mov ebx, ecx
@@2:    
    pop ax
    add al, 30h
    stosb
    loop @@2
    
    xor ax,ax 
    stosb
    mov eax, ebx
    pop edx
    pop edi
    pop ecx
    pop ebx
    mov esp, ebp
    pop ebp
    ret 8
    
end start
Добавлено через 3 минуты
содрано и переведено на асм (Окулов С.М. "Программирование в алгоритмах" )
4
608 / 406 / 8
Регистрация: 26.04.2012
Сообщений: 2,065
13.06.2013, 09:58 13
Процедура сложения длинных чисел:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void SummLong(int *A, int *B)
{
    int i, k;
 
 
    if (A[0] >= B[0])
        k=A[0];
    else
        k=B[0];
 
 
    for (i=1; i<=k; ++i)
    {
        A[i+1]+=(A[i]+B[i])/Osn;
        A[i]=(A[i]+B[i])%Osn;
    }
 
    if (A[k+1]>0)
        A[0]=k+1;
    else
        A[0]=k;
}
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
SummLong:
    push ebp
    mov ebp, esp
    pushad
    
    mov edi, [ebp+8]
    mov esi, [ebp+12]
    
    mov eax, [edi]
    cmp eax, [esi]
    jb SL1
    mov ebx, eax
    jmp short SL2
SL1:
    mov ebx, [esi]
SL2:
    mov ecx, 1
SL3:
    cmp ecx, ebx
    ja SL4
    push ebx
    mov ebx, Osn
    xor edx, edx
    mov eax, [edi+ecx*4]
    add eax, [esi+ecx*4]
    adc edx, 0
    div ebx
    add [edi+ecx*4+4], eax
    mov [edi+ecx*4], edx
    inc ecx
    pop ebx
    jmp short SL3
SL4:
 
    mov edx, ebx
    xor ebx, ebx
    cmp dword ptr [edi+edx*4+4], 0
    seta bl
    add [edi], ebx
    popad
    leave
    ret 8
Добавлено через 16 минут
программа вводит два числа, складывает их и выводит результат на экран.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
.386
    .model flat,stdcall
    option casemap:none
    include \masm\include\windows.inc
    include \masm\include\kernel32.inc
    include \masm\include\msvcrt.inc
    includelib \masm\lib\msvcrt.lib
    includelib \masm\lib\kernel32.lib
 
.data
    MaxCount = 10000
    Osn=10000
    count=4
    buf db MaxCount*3 dup (0)
    fmts db '%s', 0
    fmti db '%i', 0
    NewLine db 13, 10, 0 
msg1 db '1-e chislo:',13, 10, 0
    msg2 db  '2-e chislo:',13, 10, 0
    msg3 db 'rezultat:',13, 10,  0   
    A dd MaxCount dup (0)   
    B dd MaxCount dup (0)   
.code
start:  
    push 0
    push offset msg1
    push offset fmts
    call crt_printf
    add esp, 8
    push offset buf
    push offset A
    call ReadLong
    push offset msg2
    push offset fmts
    call crt_printf
    add esp, 8
    push offset buf
    push offset B
    call ReadLong
    push offset B
    push offset A
    call SummLong
    push offset msg3
    push offset fmts
    call crt_printf
    add esp, 8
    push offset A
    call WriteLong
    call crt_getchar
    call crt_getchar
    call ExitProcess
 
ReadLong proc   
    push ebp
    mov ebp, esp
    pushad
    mov esi, [ebp+12]       ;буфер для ввода строки
    push esi
    push offset fmts
    call crt_scanf          ;вводим строку цифр
    add esp, 8
    push [ebp+12]           
    call crt_strlen
    add esp, 4
    mov ecx, eax
    mov edi, [ebp+8]        ;адрес массива для хранения  числа
    mov dword ptr [edi], 0  ;первый элемент массива будет хранит кол-во элементов массива
                            ;в которых лежат числа
;сия конструкция протаскивает старшую цифру числа в конец.                          
@@1:                        
    push ecx                
    mov ecx, [edi]      
    cmp ecx, 1              
    jb @@3
    
@@2:    
    mov eax, [edi+ecx*4]        
    mov ebx, 10
    mul ebx
    mov ebx, Osn
    div ebx
    add [edi+ecx*4+4], eax
    mov [edi+ecx*4], edx
    loop @@2
@@3:
    xor eax, eax                ;прибавляем очередную цифру
    mov al, [esi]
    sub al, '0'
    add [edi+4], eax
    mov ecx, [edi]
    xor ebx, ebx
    cmp dword ptr [edi+ecx*4+4], 0  ;увеличим кол-во использованых элементов массива, если был задействован еще один
    setne bl
    add [edi], ebx  
    inc esi
    pop ecx
    loop @@1
    popad
    mov esp, ebp
    pop ebp
    ret 8
ReadLong endp
 
WriteLong proc 
    push ebp
    mov ebp, esp
    pushad
    mov edi, [ebp+8]
    mov ecx, [edi]
    push ecx
    push [edi+ecx*4]
    push offset fmti
    call crt_printf         ;выводим первые цифры числа
    add esp, 8
    pop ecx
@@1:
    dec ecx
    cmp ecx, 1
    jl @@4
    push ecx                
    push offset buf
    push [edi+ecx*4]    ;берем очередной элемент массива
    call IntToStr       ;преобразуем в строку    
@@2:          
    cmp eax, count ;если кол-во цифр в строке меньше, чем должно быть, то выводим нули
    je @@3   
    push eax
    push '0'
    call crt_putchar
    add esp, 4   
    pop eax
    inc eax
    jmp short @@2
@@3:
    push offset buf         ;вывод строки с цифрами
    push offset fmts
    call crt_printf
    add esp, 8
    pop ecx
    jmp short @@1
@@4:    
    popad
    mov esp, ebp
    pop ebp
    ret
WriteLong endp
 
;сия процедура есть в прикрепленных темах. отличие лишь в том, что цифры не выводятся на экран, а сохраняются 
;в буфер для последующего вывода.
IntToStr:
    push ebp
    mov ebp, esp    
    push ebx
    push ecx
    push edi
    push edx
    mov edi, [ebp+12]
    mov eax, [ebp+8]
    xor ecx, ecx
    mov ebx, 10
@@1:
    xor edx, edx
    div ebx
    push dx
    inc ecx
    or eax, eax
    jne @@1
    
    mov ebx, ecx
@@2:    
    pop ax
    add al, 30h
    stosb
    loop @@2
    
    xor ax,ax 
    stosb
    mov eax, ebx
    pop edx
    pop edi
    pop ecx
    pop ebx
    mov esp, ebp
    pop ebp
    ret 8
SummLong:
    push ebp
    mov ebp, esp
    pushad
    
    mov edi, [ebp+8]
    mov esi, [ebp+12]
    
    mov eax, [edi]
    cmp eax, [esi]
    jb SL1
    mov ebx, eax
    jmp short SL2
SL1:
    mov ebx, [esi]
SL2:
    mov ecx, 1
SL3:
    cmp ecx, ebx
    ja SL4
    push ebx
    mov ebx, Osn
    xor edx, edx
    mov eax, [edi+ecx*4]
    add eax, [esi+ecx*4]
    adc edx, 0
    div ebx
    add [edi+ecx*4+4], eax
    mov [edi+ecx*4], edx
    inc ecx
    pop ebx
    jmp short SL3
SL4:
 
    mov edx, ebx
    xor ebx, ebx
    cmp dword ptr [edi+edx*4+4], 0
    seta bl
    add [edi], ebx
    popad
    leave
    ret 8    
end start
Добавлено через 20 часов 0 минут
умножение длинного числа на короткое
Под коротким числом понимается число меньшее основания системы счисления.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void MulShort(int *A, int k)
{
    if (k == 0)
    {
        A[0]=1;
        A[1]=0;
    }
    else
    {
        int i, n, m;
        n=0;
        for (i=1; i<=A[0]; ++i)
        {
            m=A[i];
            A[i]=(A[i]*k+n)%Osn;
            n=(m*k+n)/Osn;
        }
        A[i]=n;
    }
    if (A[A[0]+1] > 0)
        ++A[0];
}
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
MulShort:
N equ dword ptr [ebp-4] 
    push ebp
    mov ebp, esp
    sub esp, 4
    pushad
    mov ecx, 1  
    mov esi, Osn
    mov ebx, [ebp+12]
    mov edi, [ebp+8]
    mov N, 0
    cmp ebx, 0
    jne MS1
    mov dword ptr [edi], 1
    mov dword ptr [edi+4], 0
    jmp short return
MS1:
    cmp ecx, [edi]
    ja MS2  
    mov eax, [edi+ecx*4]
    mul ebx
    add eax, N
    adc edx, 0
    div esi
    mov [edi+ecx*4], edx
    mov N, eax
    inc ecx
    jmp short MS1
MS2:    
    mov [edi+ecx*4], eax
    xor ebx, ebx
    or eax, eax
    setne bl
    add [edi], ebx
return:
    popad
    add esp, 4
    leave
    ret 8
программа считает 30!
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
.386
    .model flat,stdcall
    option casemap:none
    include \masm\include\windows.inc
    include \masm\include\kernel32.inc
    include \masm\include\msvcrt.inc
    includelib \masm\lib\msvcrt.lib
    includelib \masm\lib\kernel32.lib
 
.data
    MaxCount = 10000
    Osn=1000000000
    count=9
    buf db MaxCount*3 dup (0)
    fmts db '%s', 0
    fmti db '%i', 0
    NewLine db 13, 10, 0    
    A dd MaxCount dup (0)   
    B dd MaxCount dup (0)   
.code
start:  
    push 0
    mov A[0], 1
    mov A[4], 1
    mov ecx, 30
_loop:  
    push ecx
    push offset A
    call MulShort
    loop _loop
    push offset A
    call WriteLong
    call crt_getchar
    call crt_getchar
    call ExitProcess
 
ReadLong proc   
    push ebp
    mov ebp, esp
    pushad
    mov esi, [ebp+12]       ;буфер для ввода строки
    push esi
    push offset fmts
    call crt_scanf          ;вводим строку цифр
    add esp, 8
    push [ebp+12]           
    call crt_strlen
    add esp, 4
    mov ecx, eax
    mov edi, [ebp+8]        ;адрес массива для хранения  числа
    mov dword ptr [edi], 0  ;первый элемент массива будет хранит кол-во элементов массива
                            ;в которых лежат числа
;сия конструкция протаскивает старшую цифру числа в конец.                          
@@1:                        
    push ecx                
    mov ecx, [edi]      
    cmp ecx, 1              
    jb @@3
    
@@2:    
    mov eax, [edi+ecx*4]        
    mov ebx, 10
    mul ebx
    mov ebx, Osn
    div ebx
    add [edi+ecx*4+4], eax
    mov [edi+ecx*4], edx
    loop @@2
@@3:
    xor eax, eax                ;прибавляем очередную цифру
    mov al, [esi]
    sub al, '0'
    add [edi+4], eax
    mov ecx, [edi]
    xor ebx, ebx
    cmp dword ptr [edi+ecx*4+4], 0  ;увеличим кол-во использованых элементов массива, если был задействован еще один
    setne bl
    add [edi], ebx  
    inc esi
    pop ecx
    loop @@1
    popad
    mov esp, ebp
    pop ebp
    ret 8
ReadLong endp
 
WriteLong proc 
    push ebp
    mov ebp, esp
    pushad
    mov edi, [ebp+8]
    mov ecx, [edi]
    push ecx
    push [edi+ecx*4]
    push offset fmti
    call crt_printf         ;выводим первые цифры числа
    add esp, 8
    pop ecx
@@1:
    dec ecx
    cmp ecx, 1
    jl @@4
    push ecx                
    push offset buf
    push [edi+ecx*4]    ;берем очередной элемент массива
    call IntToStr       ;преобразуем в строку    
@@2:          
    cmp eax, count ;если кол-во цифр в строке меньше, чем должно быть, то выводим нули
    je @@3   
    push eax
    push '0'
    call crt_putchar
    add esp, 4   
    pop eax
    inc eax
    jmp short @@2
@@3:
    push offset buf         ;вывод строки с цифрами
    push offset fmts
    call crt_printf
    add esp, 8
    pop ecx
    jmp short @@1
@@4:    
    popad
    mov esp, ebp
    pop ebp
    ret
WriteLong endp
 
;сия процедура есть в прикрепленных темах. отличие лишь в том, что цифры не выводятся на экран, а сохраняются 
;в буфер для последующего вывода.
IntToStr:
    push ebp
    mov ebp, esp    
    push ebx
    push ecx
    push edi
    push edx
    mov edi, [ebp+12]
    mov eax, [ebp+8]
    xor ecx, ecx
    mov ebx, 10
@@1:
    xor edx, edx
    div ebx
    push dx
    inc ecx
    or eax, eax
    jne @@1
    
    mov ebx, ecx
@@2:    
    pop ax
    add al, 30h
    stosb
    loop @@2
    
    xor ax,ax 
    stosb
    mov eax, ebx
    pop edx
    pop edi
    pop ecx
    pop ebx
    mov esp, ebp
    pop ebp
    ret 8
    
MulShort:
N equ dword ptr [ebp-4] 
    push ebp
    mov ebp, esp
    sub esp, 4
    pushad
    mov ecx, 1  
    mov esi, Osn
    mov ebx, [ebp+12]
    mov edi, [ebp+8]
    mov N, 0
    cmp ebx, 0
    jne MS1
    mov dword ptr [edi], 1
    mov dword ptr [edi+4], 0
    jmp short return
MS1:
    cmp ecx, [edi]
    ja MS2  
    mov eax, [edi+ecx*4]
    mul ebx
    add eax, N
    adc edx, 0
    div esi
    mov [edi+ecx*4], edx
    mov N, eax
    inc ecx
    jmp short MS1
MS2:    
    mov [edi+ecx*4], eax
    xor ebx, ebx
    or eax, eax
    setne bl
    add [edi], ebx
return:
    popad
    add esp, 4
    leave
    ret 8
end start
результат: 265252859812191058636308480000000
для особо скептичных: факториал 9999:
Кликните здесь для просмотра всего текста
Код
2846259680917054518906413212119868890148051401702799230794179994274411340003764443772990786757784775815884062142317528830042339940153518739052421161382716174819824199827592418289259787898124253120594659962598670656016157203603239792632873671705574197596209947972034615369811989709261127750048419884541047554464244213657330307670362882580354896746111709736957860367019107151273058728104115864056128116538532596842582599558468814643042558983664931705925171720427659740744613340005419405246230343686915405940406622782824837151203832217864462718382292389963899282722187970245938769380309462733229257055545969002787528224254434802112755901916942542902891690721909708369053987374745248337289952180236328274121704026808676921045155584056717255537201585213282903427998981844931361064038148930449962159999935967089298019033699848440466541923625842494716317896119204123310826865107135451684554093603300960721034694437798234943078062606942230268188522759205702923084312618849760656074258627944882715595683153344053442544664841689458042570946167361318760523498228632645292152942347987060334429073715868849917893258069148316885425195600617237263632397442078692464295601230628872012265295296409150830133663098273380635397290150658182257429547589439976511386554120812578868370423920876448476156900126488927159070630640966162803878404448519164379080718611237062213341541506599184387596102392671327654698616365770662643863802984805195276953619525924093090861447190739076858575593478698172073437209310482547562856777769408156407496227525499338411280928963751699021987049240561753178634693979802461973707904186832993101655415074230839317687836692369484902599960772968429397742753626311982541668153189176323483919082100014717893218422780513518173492190114624687576983537344145601312261522139117875968836736408720793700299203827919803870237207803914031236899760815284030605111670948472222487038919999344207139583698306396223207911562404425080891991431983712044559834404755675948921210149815245454359428541439084356441998422485547853216362403009844285533182925315420655123707970581639346029624769701038874220644153662673371542870078912274934068433644288984710084064160009362393526124803797529334392876439831639031277645072247926785170082666959838952615075900734921519759265919270887320259406638211880198885474826604834225645770574397312225970067193606176351357952982179429079770532728326750148802444352868164502616566283754651900617187344226043891929850607151539003110668472736013581670643786175675743918437647965813610059963868955233464878174614324357322486432679848198145843270303589550842053478849336458248259203328808902578238823326577020524897093704721021424841334246526820680673231421448385407418213962184687010835958294696523563276487047571835161687923506836627174371191572336114307012112076760869785155972184648598591864364171685089962551682091079357023111851817477501080462258552131476489749066075287708289766751495100968232968973200062239288805665803614031128546592908407803397490066495320587316494809388381619865885082738246803489786475711667989042356801830350413387573197263089790943571068779730163391808786847494363353389337358690640584841782806519627582643442925805842221294764940294862267076183298822900407239040373316820741741325165668844307933944701920890562078838758534251282095735930701819770834016381763827856253951682542664461494104471157953326237281546879408042371858742302620026422182269418862621210729777665740101837618228013685758644218586301153984371229910701009406192941322320277319395946700671369537709789777811828824244292086481613417956201747183160968766104314049795819823644580736820940402221118153005143338707660706314961610777111744805955276434833338574404021275703185152729837743592187855855279559102866445791736200722185814330997729477892372071794285775627130092398239792195758119726474264287826668235391568785727162014619224426626670840076566562580710947439874011077281166991880626872662656558334566500789030905065607463307802715853081769122377281351058452732659162621964762057143488021563081525900534372114100030303924286645720732847348171203416818632896886504828736793339844397123673508452734019630942769765268417017499075694798275782583522999431563332210743913155012445900532470268031291239229797903041758782339862237353505464264691350250395100923928658510868208807066273473320035499572039708648806604092985460700633940988583634986546613672788074876470070245879011804651829611127709060901615202211146154315831766995706097461808535939040006789287854882785093863735370390404941268461899127287156265500127083303995025787993170543188275265922581494895074663997600731692731083173588305661261478299766318807006304463242911226069193127888156622159152327045769586751282199093894268660196390448971891859747292531032248021054384104432582847283058429780416240510811032691400190056878439634150269652104892027214023216023489858882737142869533968175510628747090747371818801422348724849855819843909465170836436899430618965024328835327966719018452762055108570762620424450962332320474470783119043449935144262550170177101737955112474615947173186270156557126629585512507771173833820841970589336732372445328045653717851496030880258028406784780941464183865922665280686797884325066053794304625028710510492934726747126749989263462735816714693506049511034075540465817039348104675848562596776795976829940933402638726937836532091228771807745115262264254877183546110888636084327280622777664309728387905672861803604863346489337143941525025945965250152095953615797713559579496572977565090269442808847976127666484700361964890604376193469427044407021531794358383105140491546260872848667875054167414673164899935638131286693142761686353730563458662695789456827506581023595081488877895507393936534193736570084831850447568221544406759920313807707353997803633926733454954929666875992253089389808643060653296179316402961249267308063803187391259615113189035935126648081856836677028653774239074658239091095551717977058079778928975249023073780175314268036391424472025772889178495007811788933662975043680421466819782427298069757939174222945668318581567681628879787062453124665172762275829549342148365886891929958740209569600024356030528982986638689207699283403054971026651432230612523191513184387690382370620539920693394371688046642971147674356448637502684769814885310535406332884506201217330263067648132293156104355194176105071244902487327727311209194586513749319096516249769165755381219856643220797866630039893866023860735785811439471587280089337416503379296583261843607313332752602360511552422722844725146386326936976376251019671438012569122778442842699944082915221590469443728249865808520518657629299277550883312867263841871327778087444664387535264473356244113944762878097465068395298210817496795883645227334469487379347179071006497823646601668057203429792920744682232284866583952221144685957285840386337727803022759153049786587391951365024627419589908837438733159428737202977062020712021303857217593321116241333042277374241635355358797706530964768588607730143277829032889479581840437885856777293209447677866935753746004814237674119418267163687048105691115621561435751629052735122435008060465366891745819654948260861226075029306276147881326895528073614902252581968281505103331813212965966495815903042123877564599097329672806668384916625794974792290536184556374103479143077156116865048429249028110299252967873529876782926904078877848026247922275073594840581743908625187794689004594206016860514277224448627246991114620014988066272353883780938062854438476305323507013202802948839200813213544645005613498701783427110615817728981929065649868808104556223370306725425127727733028349843359577257595622470370779338714659303308862969944031833266579751467650271734629888377739784821870071802674126599715872803544047843247867490712792167289852358848694354669225510133760637791516459725425711696847733995115899834908188828126398440050554621006698879261455821456531969690982725393451576040861347625877816586729441077535882416231577908253805474693354058246971767432452345149848302717039654388773763735819173658245427334749042426294601129988191656371384711184915691505476814041174980145426571239420442544102807580600138819865061375928853903892264432294799028648284009959867596358099911269536760152717308685275657214758350712229829652956491783507175083574136228254505562027096941747679925922977488862741131458767614753145689532809311705269648641018740767329698664923643738256547502281647192681555988319662984830777666684062231431588438491051905828181674076446303330011971029303645586659465186907447525083784198762299041591179368279976065418608872162665488649234439103092325691063377596973905178112276466848679173604940439370333935190060938726839729924647848372727477097746669359978485712015678900024194726922097498412732314740154998092038145982141648117635714780155423159966783853485448640693641055691353133523118405358134894093819182189869482538396098994282202759933963520621770534357207339625057421676946510160849560143930324430427157609952730868460920442222610315422998444480211009816133382482737521899873820531516492713449810595015997480057159191220215448774875010347324619063394130303089239941198500622590218416440998817321432442210855424862089625026060439818018902631778114661745499977144066523286384636384700165561815386109818811118173419130550502486034585675558563751172977429932907494423657966833270091836733897734790175924888566037995277154056908301731172389414032615961229291222519109594874380567338127853861649184278693841755689804710085986837203361517515809702256627520016095619222992540175987852203854591377178397638981119848580329104875166692119510451489667776159824946872742066343759320785261892268728552767132488326779415291283916540796834419023909480367668870783801136704275397139620142478493519673530144440403782352667443755674088302522574527380620998045123318810272901204299798900542312621796813523775804116251145917599327913417650729282676223689729196052828967522352142523421724784186931739746041187763460462563713530980159061773675871533680395855905482736187611215138467343288432509004564535818668190510873179134621573033954058098717201384437709927953279767553109938136584040355679573189414197651143632552627063974314652634812003272009675566770192624258505777061789379823109698678844854665952732706167030891827720643255191939367359134603775708319318084592956515887524459760172945572050559508592917550651011566507552163514231815354817688419603208505087149627049401768418398058259403818259398646126027595424743337622625628715391606902509898507079866062173220016359393861147539456140663567571852661703147145351675300749921386520776852382488460062373589660805495165240648054729586991869435881119783368014148807832121345715236012406592220850891295690783537057673467166786378090881128345039578481221210111725071838335908388618757466120131729821713107294473765626517231069488442549836951414738389247774232094020783120080723532628805390626601818605042493878867787249550325542428422659627105069264607176746750233780567189345011073737703411934611337403386536467513673366139473155021145710467116144525332485019790108343164198999841404504490113016375952067571556750948524358026910407763721099867162425479538531285288993095657072921867352321666609787498963536261052982147256948279999622082577584098845848425039118944760872968518498397636791824226657116716658015791450081165719220023375976531749592239788498281470550619068927562521046218566130580025560797460972671503332703231002527464042875555654688376583880254322740350743168427862063769705479172648437817444636152057093322858728431569075625556930555881882260359000673933995250437988747093507927618111627630977125798397599652661212031749588205943575488386228250840140888572058399240097121921254807409775297427877591256602644348271364723184912518086627870862611669998963481240580368479458736482012465366322888901163657227088775773615200345010226889018910167357205866141001172366476265783539636429781901164705617027963192233229422873930923333074825893762619899759653008413538324112589963962944512908280202322549893662750649953083892563224679469596066904690668629264500621974012178289987297970485902177506009289332895727239201958999447194514736085077040072571743931814846190940626954528503052634100056502222615230936488288712204645426770057714899433514716250425236517371026606864725345812018668327395368254745653655359754668578870005698836028668645074025699308748344109408608630370790829524057673168494185581048247530475892339280157130282410623499994593239052140985655956566134600339615051516475885274221473251799954897799284952274602985566670081187120085615501645740048417021030303899633925333746655681782441073740933691929410463230773199475982630738349960077037241044628541464870411627389564983455516216568511455138382204700548399667170624646756610129138204890912111722938624425315891306698746204558724480605282937814830262216454228042175776076236545982822307081550346940493831775505330509469899947611941923128072180721696437843331360676067696518713839433877248549368906184570057204369666646508073449581449596630624669867983287258630006421522021017181391732527517367226262145494546850600633469271383831171584975309264325248696022005909980266376538622546326516841496330636954808655110125675771789061669475834404348621848536959160217203045618349752416203992644133165188476860683064200485855792447334029014258887640371251864222901633369158506327372719959636291278334478621888787100953375355105468898023637826371492691328956433944089947012145213457211771565759145173489519501680062135392717541984387616354347980692088666622709951237170624192491428257645312576993973534167304686458518197966823201569379268492699998399241357194149688227370402282080517180800340048061526179201397894518629529055844070373830053355242115390338518582936677919061011630623367314441920289385720185556959633083361545029042482230929708712478800201738307206048268015667539759378993179351579995892956215630733841629459990027673083282771659506421796652319043925054322675373181175531547678073947033893118510729772431837897267495745577818334549594231735355829104696731539127597568728186169116108315633723263996888149054394326119718227499679117662855340186019831580962998179110720880499229201606205906727127359946187163494577499580533794718710545645257939602421025913641552839839520177301271251489205106170822800833998566578664692073711426968230177041632482947940955869469908937916519100630518535210234518979812761914306186436270308197712499275105673290948120205774710068770337970893422920718390374416750349381883634222928494679066028567429325164256904436347308765679705659567728529108124273315440658019980271157912625417279745286257486592193329380591523952473551888711986039131965428757629019050396408356024627753431440915564218172945994159606197962263324271586342597794734868207480202153873472970799975333298778553105382016216979188038075300633435076614773713593936265190522224252814108474704529568864775791350216092204034844914995077874310718965572549265128269348951579507548617234139461036517661675032994864224403965951188226498131592508018512638663530862222349109462905931782940819564048470245653830543205650692442267186325530764076187208678039171135636350126952509129102049604282323262899650275895105284436817741573094187489442806542756143097582812769812493699331302894667056041408430894223114091272223814847036434101963041363073677106003815959082974641011442135832104257435835022073717321974508903557318735044582723877072827140616299791962935722410447715505165253586754410939507921836901526113844038268005415092434651171143647789944455399365366772758956571398750554299082458560951003693466310067371470802992765693343550092718985405010991747497999155439203190896196761544468604817540069568947146392824538380701044418104550617130516058435581752103233846582920107103006112428340745860700606019483055136486702102036470847080742270437189370696568879561792871304522451684202740202196641560528033506129355873907939352440409258424838060717744460996403522189102296190903256904238137449249490689231433088422439963139639154585406528632646880758114874837140828417645522638631352026489401626249480238856823159910295262033712644927990193821113451844638754451639123937797419057664991176423763772228280231846573805012127780968031569147726491025750350875879224811022354452441087244856570075518713214659209354850455282917074959677540445077949483637175606232692575741281311024191037333808043432531088469483155572940226539497291381758133861945705779956180875595141364490761310961715592837658584003648937407682225752393598873108168966768828740383719282769043151410699767830381908569071309193134084601951114748276635072467653492204005862667763293551663193962249897991270800446598226489912522681312430052810499505859567652712359149444261255443761864502920288135858287178957722411638081516183160312972879698748013982862164562919615309635833731361972477333235302546657119690261123738062903024290427579454903002266084744651316174169191685174646494545969600533088525279208347249523547311067410909922354105550629968764215395124935598631134666172511689078563332893556915044948518911348830187636510063850256591643302192856559626391438289506832483872716561656011153151705522295576594497245478881553231641745326716797886114116535559758833197963807096299888076730361694031773644814042786778425123244997469342134821717959519069820460299717200117485730388971920559741474245301113586976625660777097022563326170110846378479555525850457805887944075606497412797453091841840520755852646220882148364675465223760921078753919045468485234975998604494332282807312067992240247750751410589077462733431909125545135222532927591384204738460305616315423655293531227838975944651578733734346317228000103138042548140402209058040505600386093740343506886308143468384890070893856505002756905967806940469843518453513414103161513368304371478664292538971716597862901072840075893970038831774264816372511327736992682770946534258359611188195509246206215397812119724476262377153445204806981908252494396396225111383117742897853582559083249048049751604710425756975344255151577981560037084723060348475397751368839040431601748624887133931181852302942542567620248568839397083674878845378917257414515591791903539853507720090059497935293945963121344550336826069005982871772353337522194191554730374206234326289296839701505889219111204924986479205341087234911543098718216005576220907573230462610659774494765834631302559863631502995967235247694397546253020678819330437228480020930535415564066483856937814460313869756345920023346260699595551348475414789118083032981642158745292295267893792564775202905267534935667374429318267337457164246540774826790104677875908540813053144717645586989416966894043648995246524744398834958387120629648541335755381341950049874381336906270397387458660429687159582071576659982660731700562446554176302450134915956728894261974614449690867165585978272922870272377483509736290101913041781273577303778180408158913600520731580694103430500318434934236026924473306001386111978177447266960892832105254311649603342010203260386367253288964833340586220484361657536200146840547664966647356697957295339480913826370332422093083936695498068824049162206314791149464204250002245041342555856193744290525725243632005448744152430730521507049102043407657247686509575117412541372953164452176557723534860182156683335252053283000010834400876226684381702323560564515825695417735919781364997555960191256774494271798636004584740520929008939731527602430495165386443138814787697754147875743261015987970975885562580676619797309847246076948482112794842797653660705505163910441502255442032972129203300935335668729459591232796588637648689418843364054849400957496579165768721392733015355509786511476794739969062318487837751546261382365166595633720934570820830184048279700572807143292572757743622958704736164160973181724159420427036606640408974024552153072522738863724185964645522367326041116459846402001021692082331515538882107152719126787653179507190820452510044782129131854405481449415186711420710369389112912501275085346633771774937601654345469639004271112982925509683042066572536427947220002083531388370878164995718971762933879485427127688265200376632592456161486874489747151936621927566585246211445740701067538042756418444083480520383826505260169858406008478842242188785692789775181044280547442722945516742033568646060997797312495043332142520505367579049952078359765041537900113257953604065517265487902217359544415113942923164895066317781303905746208244917192131186412963370466140645690017894235673877552313095278591277453324185544248448449366421073134881918064018922231730215664581347318644999790578166209146987071803938888578128074022636360229411435486987140214357205594773089280865367892020193510260536156792448327674947611785831607186571031084220056025954511519139130911954444784436103274187610233884339168758923342379085984196826652561062875123757231849147495194598572889793498179176182265248040823712810979077263886428606791708228857585270347083971456161992624784479469279499684594563238270229736417350343078319411569824782001329085120287847480586018896004590174597405563073271448767908528886797880997069524068100662561144001498341358088973724684406494885707416768791641322420537365406733018639249791091547478595916386559750709058117592489950221479925094563558251431581446406013428349042279835793965925898520076384564668164073268192834600776728587628490006887456463927496441590403403367233781449159703294178729415506105412951540015939385166392932567742955754948004665827357965399094023354364464937682727254187362754753297680819032533614108643308423777173899522153676309530204590243869463270289529399448301357758908121488455849381987450592091406720952246909626307694175334098369885936370031497372897799636001862650017492929008793118999782296371230664229799616358257260011228898364765141804597577004212083394936465964733646428904449932539622709190737370577205132281595786322759191278605429786295318861555980472816071086413280358540016005557568685579178597789919790265659262128300722535140152597356930072901539221111686850474040217217444205173800025136100049453411932433166834424312596309881239696220235885839558783168519483312665357735324437993568321526917704224903457453485891381258268136690892947680905263556063811966130606393693841181771354592988431723291223626245886839420288998169356116986542988477651311822766252673997880881601047065154233501567135374481708623431466253119029104015226292710409928507241884332900727779475411163755217656358931632663604938121840183751281888477116897547948376766408484275362307401954218321798549626066659034792581634239267094783990706292316653503728501975132481380383707089463892547088703908572358100613062864666471000610435211577892661343221465531141188259694292628452210902668841497576334155492113558125461655807827347011581400600834576213313038998784327065371995670957084738578609264918885837873923916555426357730129224364160406255173689233563656885436585164620782187574172436452581414348763276134175270737675492227628778226476515431534158571377352273033540337636420425803425726474968621782366695135341067737842113137113198737322289180527506281227771641249441240120712595431999174657474589258261371282555553508040414394455729599455463560848725133946293635894083209896480161958313042972096479412853938899626536892826380767716875958850221646458243094016500968879736615773356031683671038689522827094150954522274400273549925367021471599405654481384218638012879990082093357632073636940599142426371829400061374190057951309629854533074819780256830108967287380223482048886297313036968988264065790478156238977848536502569106423179573602533090876327178491118974843224686808634038396417612760578864657447228482493268744306255122050695516846466947718368191143287354481583635054814641109996014339059579976629064688129502503915092363301107607063286331739337814969338024758003505278978275575092860403942050634293932706463616103182287924815267930686274923727563185222565426600855684949772028590915093049542596747364833143723634955544890159866840836217691355965603951967042536886348236958712946252475903177681318497758827657674048255813650210364958550570325921995767533426422378372358605850940358397710347667064478864083110965030256521560746401965271699973237346523717345659551455949309816664400621159934913318013515052865184217882802634332593475585076116869770912558005618568371054085608124951940314806461871940257766328526701969838756756152469675902810686489686929331595435209768752713720161616093117425019970928968494003469624232568841066511330437741225617625865894123672817114552642389451263171783479027692117145288735295501933675921890800604863373778672818061025478257043678844950351892578749983669478590861297554308412267706095434761213371743315678379016201233723702333831641470642859218597761015823272199791506287186818675098166553774501302088033390435363977026336380909852649453262814655806554650482348642949539061325740049691288834051822293364447668385503796797580961998357580702775953596878822619465961222304454927560027495516858354258229533604283442631847806882539545074669187789776540603843251284381281131685620460861728940822965862617442076692029742793008812951985467871354862323661041321658127926715154596159435259345675744599230788920551954008231640971959125002545523750310673563974883554248044968138303067185193149133578920212360530819995202058450342349993215096263497781245665830468058182456352481462584933192619540688481844644524842948606301616947666324262523147632237110969536948382448231641039622450767540561428746826783572370489560699065279268845584451204665485337853402664664504233963848825771987495361130049421559373554521192618672147826541688560409492829005661688380763765669051074089251054916522296887867696863165251491770149990006663734454612026278070192569870622554092894519471877800430613002182828742586704874848082694857344477824407873410271082487026952383080491096048201390129402463124480015933667021265831767787975296596347257689432654043588926729395068786083062626626328739208732730254791009993211338897780781433672879144876837368646774852877773740354747287164421776782071296450627088097863792814407119250514114800490705560809722929979244147106285224702987069986922767634177351325860290890387570745436807787642238533370069208961635100923358730398654390607188095255755338036472589500730677212252807817947105648117137855745105769104432292542902414943358839609367932136169695425129973103103280443695450192984382084238312126582574059450942694277730712480217691578183572008717053877325601798713300550591137782384179164028084140962382084763739301393077842855454522236755982466625060875428487610414566136222764240591430445558085631818093523040779389161490211629240051507491406844320323036560995487862099919430656445533254713555736531851601170032155069078771675206288152788589714941032098698408304896652435103050244467993177914765910342894912905412036160169567122214080636940594030455218621287993309285623102241844636528909744464015198662318388196244482259078358591404368619301904145896269387890703498216986869693444808621399053459179282665430479820721963413475564652548314377115667845907779719651077246800029358154626764631022427900731363135252206706295112593587447313418649249728278479664458544896293290526205806524858870702087938913447608334465317093924240824932800891573131954134831182092775248688054873394331586756266612217935505119060999291137944563499562739189845902902171315570609626788167330294019846423739044509802803094897598125925205585097353743655682578031368190200715167569382728181882458754171072118080655644803912250453708942269535838219253507569283409563985926559974039131670929004399627597683037521750336087902829567306886226307772973353385368266873451903570970968732232373830049409012323927431875904652632709517840626726482889364689659321916952110636172975707437614806160133110491169227131860940414501484286642363471698289241818048436523053886455980983927383649068548082301426780314393744043180782267877949400620648915124895251654300563444837504675175420704331337248687063323756164523236048193202437759689091478337217955367699260323571518551339109840273906375328070231330175575426939620262942391094532353791012594896494181256367299296708425066759980345627345559855962851228141458255602484178330564524050845006598875598751860133586062493278448777200684229659194551653956298296059161004657890721484205486183041817560455981516808803178308026144599444467791801243214640098361067868341297487259672925878680622308011582202628901436445900230164582366670926557126455992579062230474523562557511177079151200278938097577546854612101730752279924140702630813779297190946141314580208108773812162453985876969737142588183615260506938092691771208732191500583197711332279357238507194061276129187257209940493025027774815661402132743474388196641333005263422908290640092794492480855613118344016180480135703250783632393892156764315962044261280970094410777613063890907129445639405660155924602545420477118614042015523337127050137712103457000957800938926532938572047857650877714966340300356238059575719160938217131222281046585838894350717643193997301266159142383717028440012039948588099623185947247485877658435507700693409922034037877219272837030138083814439411498497173076616296134205910501481428394970069595167693904155790285635691105554731268457149744963532055467794077518405666763722296909034612870682988710427876109009099916044382179451176362083537971616183312436443126785543555080050798612466439772413550212823802672671991498972724851298128728369748927642079286866697017725979440785815590933250855413129994658111852769165246479081911938423327589769957301209810300917100169571879161694227007952891519191252105389183853895931516740050572381740103062100438024301118797770425232807323657512960937245605368003751659616423614770933039122440975287173206797612812042802673925655730567593151264575004787575653185482582141157403047314749251191083561576573200254610968670189030764853137383291268248174118135903282662508254931321143147895335231704398905392853494664288607426837182490249809247948722663368682379958087563704080865564932190548963778554953116739793527079947045239915329753435869051410586409653451418289647443936718285271184356079928589597817654395011308884841916351667321369286083095674450280180037371645800916808297270871560918503865405343666004550498562468737602255704159580025017409536183928764345800367086495405794172008513635712716376832349313423070382127448450144052954169537438194545945653316514099099372272280101965465272622783151210346768616682613147184361002551786324795015002295369546631773958934413148148583469437452398115995466607120599779436344018507836089910894807341963393925931897394094311004211672912019972262660987192701402410580551531510010980499604414729103945103031266411472673683997331503503674274154699263316527043294067523744907505673950892967477911580086439999256481720884742925082154627985607912776861194608621034940553585013447219024454382452108928440949813271701067396647111493189678997766159548818619317690017502790178382462438787383148327950087902643399257702658800584977898462429566032127694581082434812969084097255067105473247131725499719190103955330584704072808169315862609388601914768994413767362143208360737513157437631675466647918675389657155510085062681000511982748680778059266776565410083477857102425013325339158738476102412979473675100116349897780374593002545760987067109215359711517825201428121664754303407512860024029703842861598428981660214342984908891735968219228446912303590432987723184330991418726467460755831872571313883235601580900959418253020779939764846259790188334179383092096584146357441198587829647585094305300814834182174782660377376225299770346875290351731079208322003808080921216434658681798981050427437538578678918635051771750160653182640692888325013591951717853768786588175236642153401096129576307476264807031275736578776235285905715393248457650394439049666808771189919249893389652485239553679582753061416713175791575638660600483999417954870586820920119515495203129456245131542250657485862916160652379664301017269395028229466748968174682116399679495029428401309923590127825043742819255763453321757616229275111059836827156722977862005372293231408288705874944406011623652162771755850301345147145276584186427707176996843549962025754743181199488338580675969235958062216583246409209535064835793581774290301831535129001432149551817745690838871932069776969565777175449914991143136895083616069253960646989337487094293321918560129910856447025625716350550862068924029758968471428367868473545553358347765253615657818999698306865467173644599634313646819542742049047243306467500144269750832236901308389549263706677840653132866488608012951377172084758115771949101234514177494148277358004143266733237961771696569858278583230050526588350224786805064820144457059319734338292386007260169651090325898090991283765227538149352984509941496693386281556803130698106452519270381851587264869176256323944142521611842776914506771841173571439668100561548395244315494486423838429890039982611332246896334652210469254513796927600971964533895533210558424564018744861105095911176682894271164005401050377042034605252131822804589299863790357235066510878235004334994239128523630889651098924664105633158417114288530414377228662983231897086903040030132595147677423751615884091583805915167350451913117819394342848292227230406142258207802782914807042676162930253922832108491775998420059510531216473181840949313980044407284732590260916973099815385393903128087882390294800157900800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000


подсчет факториала любого числа меньшего 1000000000 (проверял только на 50000)
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
.386
    .model flat,stdcall
    option casemap:none
    include \masm\include\windows.inc
    include \masm\include\kernel32.inc
    include \masm\include\msvcrt.inc
    includelib \masm\lib\msvcrt.lib
    includelib \masm\lib\kernel32.lib
 
.data
    MaxCount = 50000
    Osn=1000000000
    count=9
    buf db MaxCount*3 dup (0)
    fmts db '%s', 0
    m dd ?
    fmti db '%i', 0
    NewLine db 13, 10, 0   
    A dd MaxCount dup (0)   
    B dd MaxCount dup (0)   
.code
start:  
    push 0
    mov A[0], 1
    mov A[4], 1
    push offset m
    push offset fmti
    call crt_scanf
    mov ecx, m
_loop:  
    push ecx
    push offset A
    call MulShort
    loop _loop
    push offset A
    call WriteLong
    call ExitProcess
 
ReadLong proc   
    push ebp
    mov ebp, esp
    pushad
    mov esi, [ebp+12]       ;буфер для ввода строки
    push esi
    push offset fmts
    call crt_scanf          ;вводим строку цифр
    add esp, 8
    push [ebp+12]           
    call crt_strlen
    add esp, 4
    mov ecx, eax
    mov edi, [ebp+8]        ;адрес массива для хранения  числа
    mov dword ptr [edi], 0  ;первый элемент массива будет хранит кол-во элементов массива
                            ;в которых лежат числа
;сия конструкция протаскивает старшую цифру числа в конец.                          
@@1:                        
    push ecx                
    mov ecx, [edi]      
    cmp ecx, 1              
    jb @@3
    
@@2:    
    mov eax, [edi+ecx*4]        
    mov ebx, 10
    mul ebx
    mov ebx, Osn
    div ebx
    add [edi+ecx*4+4], eax
    mov [edi+ecx*4], edx
    loop @@2
@@3:
    xor eax, eax                ;прибавляем очередную цифру
    mov al, [esi]
    sub al, '0'
    add [edi+4], eax
    mov ecx, [edi]
    xor ebx, ebx
    cmp dword ptr [edi+ecx*4+4], 0  ;увеличим кол-во использованых элементов массива, если был задействован еще один
    setne bl
    add [edi], ebx  
    inc esi
    pop ecx
    loop @@1
    popad
    mov esp, ebp
    pop ebp
    ret 8
ReadLong endp
 
WriteLong proc 
    push ebp
    mov ebp, esp
    pushad
    mov edi, [ebp+8]
    mov ecx, [edi]
    push ecx
    push [edi+ecx*4]
    push offset fmti
    call crt_printf         ;выводим первые цифры числа
    add esp, 8
    pop ecx
@@1:
    dec ecx
    cmp ecx, 1
    jl @@4
    push ecx                
    push offset buf
    push [edi+ecx*4]    ;берем очередной элемент массива
    call IntToStr       ;преобразуем в строку    
@@2:          
    cmp eax, count ;если кол-во цифр в строке меньше, чем должно быть, то выводим нули
    je @@3   
    push eax
    push '0'
    call crt_putchar
    add esp, 4   
    pop eax
    inc eax
    jmp short @@2
@@3:
    push offset buf         ;вывод строки с цифрами
    push offset fmts
    call crt_printf
    add esp, 8
    pop ecx
    jmp short @@1
@@4:    
    popad
    mov esp, ebp
    pop ebp
    ret
WriteLong endp
 
;сия процедура есть в прикрепленных темах. отличие лишь в том, что цифры не выводятся на экран, а сохраняются 
;в буфер для последующего вывода.
IntToStr:
    push ebp
    mov ebp, esp    
    push ebx
    push ecx
    push edi
    push edx
    mov edi, [ebp+12]
    mov eax, [ebp+8]
    xor ecx, ecx
    mov ebx, 10
@@1:
    xor edx, edx
    div ebx
    push dx
    inc ecx
    or eax, eax
    jne @@1
    
    mov ebx, ecx
@@2:    
    pop ax
    add al, 30h
    stosb
    loop @@2
    
    xor ax,ax 
    stosb
    mov eax, ebx
    pop edx
    pop edi
    pop ecx
    pop ebx
    mov esp, ebp
    pop ebp
    ret 8
    
MulShort:
N equ dword ptr [ebp-4] 
    push ebp
    mov ebp, esp
    sub esp, 4
    pushad
    mov ecx, 1  
    mov esi, Osn
    mov ebx, [ebp+12]
    mov edi, [ebp+8]
    mov N, 0
    cmp ebx, 0
    jne MS1
    mov dword ptr [edi], 1
    mov dword ptr [edi+4], 0
    jmp short return
MS1:
    cmp ecx, [edi]
    ja MS2  
    mov eax, [edi+ecx*4]
    mul ebx
    add eax, N
    adc edx, 0
    div esi
    mov [edi+ecx*4], edx
    mov N, eax
    inc ecx
    jmp short MS1
MS2:    
    mov [edi+ecx*4], eax
    xor ebx, ebx
    or eax, eax
    setne bl
    add [edi], ebx
return:
    popad
    add esp, 4
    leave
    ret 8
end start
5
Ушел с форума
Автор FAQ
15840 / 7422 / 994
Регистрация: 11.11.2010
Сообщений: 13,385
25.06.2013, 05:06  [ТС] 14
несколько способов создания файла в DOS
  1. Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    ; masm dos com #
    .model tiny
    .286
    .CODE
    org 100h
    start:  mov ah,3Ch
            mov cx,0; атрибуты файла
            mov dx,offset filename
            int 21h
            retn
    filename db 'myfile.txt',0
    end start
  2. Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    ; masm dos com #
    .286
    .model tiny
    .code
    org 100h
    start:  mov ah,5Bh
            mov cx,0; атрибуты файла
            mov dx,offset filename
            int 21h
            retn
    filename db 'myfile.txt',0
    end start

  3. Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    ; masm dos com #
    .286
    .model tiny
    .code
    org 100h
    start:  mov cx,0; атрибуты файла - обычный файл
            mov bx,2 ;режим доступа чтение-запись
            mov dx,10h 
            mov si,offset filename;указатель на имя файла 
            mov ah,6Ch
            int 21h ;создаем файл 
            retn
    filename db 'myfile.txt',0
    end start

  4. создаем файл с длинным именем (до 255 символов) и чувствительным к регистру
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    ; masm dos com #
    .286
    .model tiny
    .code
    org 100h
    start:  mov cx,0; атрибуты файла - обычный файл
            mov bx,2 ;режим доступа чтение-запись
            mov dx,10h 
            mov si,offset filename;указатель на имя файла 
            mov ax,716Ch
            int 21h 
            retn
    filename db 'myfile.txt',0
    end start

  5. через FCB
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    ; masm dos com #
    .286
    .model tiny
    .code
    org 100h
    FCB struct
            drive_num       db ?
            file_name       db 8 dup(20h)
            file_ext        db 3 dup(20h)
            block_num       dw ?
            record_size     dw ?
            file_size       dd ?
            file_data       dw ?
            rezerved        db 10 dup(?)
            current_rec     db ?
            random_rec      dd ?
    FCB ends
    start:  mov ah,16h
            mov dx,offset fcb
            int 21h
            ret
    fcb FCB <0,'myfile','txt'>
    ;0 - диск по умолчанию, далее 8-байтовое имя файла
    ;если меньше, то заполняется пробелами, далее 3-байтовое расширение
    end start

  6. создаем файл при помощи набора команд DOS
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    ; masm dos com #
    .286
    .model tiny
    .code
    org 100h
    start:  mov bx,100h     ;выделим блок памяти в 256 параграфов
            mov ah,4Ah               
            int 21h
            mov bx,offset parametrs ;указываем на блок параметров
            mov [bx+4],cs
            mov dx,offset filename
            mov ax,4B00h;загрузить и выполнить программу из командной строки
            int 21h
            retn        ;выход в DOS
    command_line db N,'/c copy nul myfile.txt',0Dh
    N = $-command_line-1;длина командной строки
    ;командная строка типа pascal, начинается с байта длины строки, заканчивается
    ;ASCII-кодом клавиши Enter (0Dh). При передаче команды CMD.EXE нужно указать /С перед 
    ;строкой (требование вызова вторичного командного процессора). Программу cmd.exe
    ;из папки windows\system32\ проще разместить в том же каталоге, что и программа 
    filename db 'cmd.exe',0
    parametrs dw 0,command_line,5 dup(0);блок параметров
    end start

  7. Загрузить COMMAND.COM и настроить его по месту в памяти не используя функцию 4Bh прерывания 21h
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    
    ; masm dos com #
    .286
    .model tiny
    .code
    org     100h
    start:  mov ah,4Ah      ;модифицируем назначенную память
            mov bx,100h     ;новый размер блока в параграфах
            int 21h         
            or bx,-1        ;требуемое число параметров для выполнения
            mov ah,48h      ;выделить блок памяти
            int 21h         ;возвращает в bx размер наибольшего доступного блока 
    ;памяти в параграфах
            mov ah,48h      ;выделить блок памяти, в bx действительно доступная память
            int 21h         ;возвращает в ах сегментный адрес выделенного блока
            mov es,ax       ;es:=new PSP
            mov ss,ax       ;ss:=new PSP
            xchg dx,ax      ;dx:=new PSP
            mov ah,26h      ;создать новый префикс программного сегмента
            int 21h         
            mov dx,offset filename
            mov di,80h      ;указатель на командную строку
            mov cx,(N+1)/2  ;копируем по два байта за раз, N округлен в большую 
    ;сторону к числу кратному два, поэтому добавочный movsb не нужен
            mov si,offset command_line
            rep movsw       ;создаем командную строку для запуска command.com
            mov ax,3D00h    ;открыть command.com на чтение
            int 21h
            xchg bx,ax      ;дескриптор файла в bx
            xor cx,cx       ;cx=dx=0
            xor dx,dx
            mov ax,4202h    ;установить файловый указатель на конец файла
            int 21h
            push ax         ;сохраним в стеке длину файла command.com
            mov ax,4200h    ;установить файловый указатель на начало файла
            int 21h
            mov dx,es       ;пересчитываем чему должен быть равен dx из расчета,
            add dx,10h      ;что cs*10h+dx=es*10h+100h
            mov cx,cs
            sub dx,cx
            shl dx,4        ;в dx адрес буфера, куда будет скопирован command.com
            pop cx          ;в сх число байтов для чтения 
            mov ah,3Fh      ;читаем command.com в буфер
            int 21h
            mov ah,3Eh      ;закрыть файл command.com
            int 21h
            mov ax,es       ;ds:=new PSP
            mov ds,ax       ;при старте СОМ-файла cs=ds=es=ss=PSP ip=100h
            push es         ;новое значение cs:=new PSP
            push 100h       ;новое значение ip:=100h
            retf            ;запускаем command.com для создания файла myfile.txt
    filename db 'c:\windows\system32\command.com',0;полный путь к command.com
    command_line db N-1,'/c copy > myfile.txt',0Dh
    N = $ - command_line
    db 0; если N нечетное, то копируется и добавочный нулевой символ 
    end     start

  8. имитируем вызов int 21h
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    ; masm dos com #
    .286
    .model tiny
    .code
    org 100h
    start: mov cx,0        ;атрибуты файла (обычный файл)
    ;получаем дальний адрес (cs:ip) обработчика 
    ;прерывания 21h из таблицы векторов прерывания
            mov es,cx;сегментный адрес таблицы векторов прерывания в es                          
            les bx,es:[21h*4];сегмент и смещение обработчика прерывания 21h        
    ;параметры для функции 3Ch прерывания 21h        
            mov dx,offset filename;имя создаваемого файла
            mov ah,3Ch      ;номер функции
    ;три параметра в стек для возврата из прерывания
            pushf           
            push cs
            push offset @f  ;адрес возврата
    ;имитируем вызов int 21h
            push es         ;cs для int 21h
            push bx         ;ip для int 21h
            retf            ;подменяем cs и ip
    @@:     ret      ;выход из программы
    filename db 'myfile.txt',0
    end start

  9. Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
    ; masm dos com #
    .286
    .model tiny
    .code
    org 100h
    FCB struct
        drive_num   db ?
        file_name   db 8 dup(20h)
        file_ext    db 3 dup(20h)
        block_num   dw ?
        record_size dw ?
        file_size   dd ?
        file_data   dw ?
        rezerved    db 10 dup(?)
        current_rec db ?
        random_rec  dd ?
    FCB ends
    start:  push offset @f    ; Занести в стек флаги, сегмент
        push cs           ;    и смещение адреса возврата
        pushf             ;    в обратном порядке.
        mov cl,16h
        mov dx,offset fcb
        jmp dword ptr ALT_DOS_PTR ; Выполнить функцию.
    @@: mov ah,4Ch
        int 21h 
    fcb FCB <0,'myfile','txt'>
    ;0 - диск по умолчанию, далее 8-байтовое имя файла
    ;если меньше, то заполняется пробелами, далее 3-байтовое расширение
    ALT_DOS_PTR dw  0C0h,0;адрес для перехода в альтернативный обработчик
    end start

  10. Перечитывая Зубкова наткнулся на еще один способ
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    ; masm dos com #
    .286
    .model tiny
    .code
    org 100h
    start:  mov si,offset string    
        mov cx,N
    @@: push cx
        lodsb
        mov ah,5      ;номер функции
        mov cl,al;передача параметра через регистр CL, содержимое CH=0 
        int 16h
        pop cx
        loop @b
        retn
    string db 'copy>myfile.tx',0Dh; не более 15 символов
    N = $ - string
    end start
    Там в главе "Основы программирования для MS-DOS" 4.4.2 Ввод с клавиатуры. Средства BIOS. Пример, который имитирует набор на клавиатуре команды DIR, через 5 функцию 16h прерывания. Но стоит примечание
    "Например, следующая программа при запуске из DOS вызывает команду DIR (но при запуске из некоторых оболочек, например FAR, этого не произойдет)."
    У меня под FAR'ом программа работала, но попытка создать файл через "COPY > myfile.txt" не работала, пока опытным путем я не установил, что длина всей строки не должна быть больше 15 символов, обязательно заканчиваться на 0Dh (ASCII-код Enter)
  11. через int 2Eh
    Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    ; masm dos com #
    .286
    .model tiny
    .code
    org 100h
    start:  mov ah,4Ah  ;allow mem alloc.
        mov bx,10h  ;новый размер блока в параграфах
        int 21h     
        mov si,offset command_line
        int 2Eh ; execute a command using BASE LEVEL
        mov ah,4Ch  ;выходим из программы
        int 21h
    command_line db N,'copy > myfile.txt',0Dh
    N = $ - command_line - 1
    end  start
    MS-DOS. РУКОВОДСТВО РАЗРАБОТЧИКА
    Прерывание 2Eh. "backdoor" для командного процессора
    Обычно для обработки команды с помощью командного процессора COMMAND.COM используется функция EXEC (функция 4Вh прерывания "int 21h"). Однако, альтернативный, быстрый и "грубый" метод выполнения той же функции обеспечивает прерывание "int 2Eh". Чтобы выполнить какую-либо команду операционной системы MS-DOS, вначале уплотняется память с целью выделения места для новой программы (как и в случае функции 4Вh прерывания "int 21h" ), затем заносят в регистр DS:SI указатель на строку параметров данной команды и в конце обрабатывается прерывание "int 2Eh". Первый байт в строке параметров данной команды - это длина строки, за ней следует сама строка (например, "FORMAT C:"), которая заканчивается символом "возврат каретки" (0Dh). Этот завершающий символ считается частью длины строки. После того как прерывание "int 2Eh" обработано, важным моментом является обнуление стека, поскольку в результате прерывания "int 2Eh" содержимое регистров SS и SP может не сохраниться.
    На самом деле символ 0Dh можно как включать в длину строки, так и не включать в неё — и в том и в другом случае программа работает. По всей видимости int 2Eh используется в Волков Коммандере (VC.COM), по крайней мере, у Всеволода В. Волкова в book.lib.rus.ec/books/VCINFO.TXT я нашел такую фразу
    9. Предусмотрено два способа запуска команд из VC. Первый из них стандартный, запускает COMMAND.COM /C <команда>. При этом лишь требуется, чтобы переменная COMSPEC в окружении DOS была корректно установлена и наличие командного процессора по указанному пути. Это для тех, кто не любит рисковать (по-умолчанию). Второй способ, с использованием прерывания int 2Eh, работает не всегда (NDOS его просто не поддерживает). Этот способ предоставляет некоторые преимущества по сравнению с первым: позволяет изменять первичное окружения DOS командами SET, PATH, PROMPT и др., не загружается вторичная копия COMMAND.COM, поэтому команды выполняются быстрее и при загрузке резидентных программ в памяти не остается дырка от COMMAND.COM. Есть и несколько недостатков, заложенных в DOS: прерывание BATCH-файла с помощью Ctrl-Break останавливает выполнение, но при попытке выполнить следующую команду, сначала продолжается выполнение прерванного BATCH-файла, а лишь потом выполняется требуемая команда; DOS не предусматривает вложенное выполнение через int 2Eh, и поэтому, если одна программа запущена через int 2Eh запустила другую программу тоже через int 2Eh, то после завершения последней, управление передается процессу, запустившему первую программу, а первая программа остается в памяти, хотя доступа к ней нет. В итоге выбор остается за Вами. Второй способ запуска команд выбирается включением опции Quick execute commands в конфигурации.

  12. Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    ; masm dos com #
    .286
    .model tiny
    .code
    org 100h
    start:  xor cx,cx; атрибуты файла
        mov es,cx
        les bx,es:[4*21h];читаем адрес текущего обработчика int 21h
        mov ofs21,bx
        mov seg21,es
        mov ah,3Ch
        mov dx,offset filename
        pushf; вызываем прерывание 21h
        db 9Ah;дальний вызов процедуры с указанием непосредственного адреса
        ofs21 dw ?
        seg21 dw ?
        retn
    filename db 'myfile.txt',0
    end start

  13. Assembler
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    ; masm dos com #
    .286
    .model tiny
    .code
    org 100h
    start:  xor cx,cx; атрибуты файла
        push ds
        mov ds,cx
        mov si,4*21h;читаем адрес текущего обработчика int 21h
        mov di,offset ofs21
        movsw
        movsw
        pop ds
        mov ah,3Ch
        mov dx,offset filename
        pushf
        call dword ptr ofs21; вызываем прерывание 21h
        retn
    filename db 'myfile.txt',0
    ofs21 dd ?
    end start
3
Ушел с форума
Автор FAQ
15840 / 7422 / 994
Регистрация: 11.11.2010
Сообщений: 13,385
25.06.2013, 13:16  [ТС] 15
Выдираю из io.sys (можно из ntvdm) кусок 21h прерывания, отвечающего за создание файла. Упрощаю и выкидываю "всё лишнее", в результате получаю СОМ-файл, создающий файл на диске и не использующий прерывания.
Кликните здесь для просмотра всего текста
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
; masm dos com #
;.286
.model tiny
.code
.386
org 100h
start:  mov cx,0;аттрибуты файла
    pushf;три параметра для iret
    push cs
    push return
    cli;вход в прерывание 21h
    push es
    push ds
    pusha
    mov ax,ds
    mov ds,cs:new_DS ; 0A7
    mov ds:[5EEh],ax;old DS
    mov ds:[5ECh],bx; сохранили BX
    mov ax,ds:[586h]
    mov ds:[5F4h],ax
    mov ax,ds:[588h]
    mov ds:[5F2h],ax
    mov byte ptr ds:[575h],0
    test byte ptr ds:[100Eh],1
    jnz @f
    mov word ptr ds:[340h],0;ax
@@: inc byte ptr ds:[323h]
    mov ds:[586h],sp
    mov ds:[588h],ss
    mov ax,ds:[332h];0F53h
    mov ds:[33Eh],ax
    mov ds,ax
    mov ds:[2Eh],sp
    mov ds:[30h],ss
    mov ss,cs:new_DS
    mov sp,7A2h
    sti
    mov ds,cs:new_DS
    mov byte ptr ss:[5F8h],0
    and word ptr ss:[613h],800h
    mov word ptr ss:[359h],100h
    mov byte ptr ss:[34Eh],0
    mov byte ptr ss:[34Ch],0
    mov word ptr ss:[33Ch],3C00h;аргумент "создать файл"
    mov byte ptr ss:[322h],0
    mov word ptr ss:[324h],01FFh
    mov byte ptr ss:[35Ah],0
    mov sp,922h
    mov bx,6CA9h
    xchg bx,ss:[5ECh]
    mov ds,ss:[5EEh]
    call sub_6CA9
    and byte ptr ss:[86h],0FBh
    cli
    mov ds,cs:new_DS
    dec byte ptr ds:[323h]
    lss sp,ds:[586h]
    mov bp,sp
    mov [bp],al
    mov ax,ds:[5F4h]
    mov ds:[586h],ax
    mov ax,ds:[5F2h]
    mov ds:[588h],ax
    popa ;выход из прерывания 21h
    pop ds
    pop es
    push ds
    mov ds,cs:[new_DS]
    test byte ptr ds:[1482h],2
    pop ds
    jmp loc_5424
 
sub_2742 proc ;из 2 мест
    mov ds,cs:[new_DS]
    lds si,ds:[586h]
    retn
sub_2742 endp
 
sub_3401 proc; копируем строку  (вызов из трех мест)
@@: movsb
    cmp byte ptr [si-1],0
    jnz @b
    retn
sub_3401 endp
sub_340A proc ;в CX длина строки на которую указывает DI 
    push di  ;(вызов из двух мест)
    push ax
    mov cx,0FFFFh
    xor al,al
    repne scasb
    not cx
    pop ax
    pop di
    retn
sub_340A endp
 
loc_4B13: mov dx,ax
    push di
    push si
    mov di,si
loc_4B19: lodsb
    or al,al
    jz loc_4B27
    call sub_4F79
    jnz loc_4B19
    mov di,si
    jmp short loc_4B19
loc_4B27: mov si,di
    push ds
    push si
    push dx
    call sub_4EB7
    pop dx
    cmp byte ptr [si],0
    stc
    jnz loc_4B3D
    push ss
    pop ds
    push dx
    push si
    push di
    push cx
    push ax
    push word ptr ds:[54Eh]
    cmp byte ptr ds:[54Eh],5
    jnz @f
    mov byte ptr ds:[54Eh],0E5h ; 'х'
@@: test byte ptr ds:[56Eh],8
    jnz @f
    mov si,48h ; 'H'
loc_4AA4: test word ptr [si+4],8000h
    jz loc_4ABC
    mov ax,si
    add si,0Ah
    mov di,54Eh
    mov cx,4
    repe cmpsw
    mov si,ax
    jnz loc_4ABC;D1
    mov ss:59Eh,ds
    mov bh,[si+4]
    or bh,0C0h
    and bh,0DFh
    mov ss:[59Ch],si
    jmp a3
loc_4ABC: lds si,[si]
    cmp si,0FFFFh
    jnz loc_4AA4
@@: stc
a3: mov cx,ss
    mov ds,cx
    pop word ptr ds:[54Eh]
    pop ax
    pop cx
    pop di
    pop si
 
    pop dx 
loc_4B3D: pop si
    pop ds
    pop di
    jb loc_4B6E
    cmp si,di
    jnz loc_4B4A
    xor dx,dx
    jmp short loc_4B70
loc_4B4A: mov al,[si]
    push ax
    mov byte ptr [si],0
    push si
    mov si,di
    cmp dl,0
    jnz near ptr unk_4B5D
    mov dl,ss:338h
unk_4B5D db 0C4h,0C4h ; -
    push ax
    inc sp
    jnb loc_4B66
    call loc_51F0
loc_4B66: pop si
    pop ax
    mov [si],al
    jnb loc_4B70
    inc dx
    clc
loc_4B6E: mov si,di
loc_4B70: pop di
    cmc
    retn
 
sub_4EB7 proc 
    mov byte ptr ss:[350h],1
    push ss
    pop es
    mov di,54Eh
    push di
    mov ax,2020h
    mov cx,5
    stosb
    rep stosw
    xor al,al
    mov dl,al
    stosb
    pop di
    mov cx,8
    call sub_4F23
    jnbe loc_4E79
    add di,cx
    jmp a6
loc_4E79: dec si
loc_4E83:: call sub_4F23
    jnz loc_4E94
    test byte ptr ss:350h,0FFh
    jz loc_4EA9
    cmp al,' '
    jnz loc_4EA9
loc_4E94: jcxz loc_4E83
    dec cx
    cmp al,'*'
    jnz loc_4E9F
    mov al,'?'
    rep stosb
loc_4E9F: stosb
    cmp al,'?'
    jnz loc_4E83
    or dl,1
    jmp short loc_4E83
loc_4EA9: mov al,' '
    rep stosb
a6: dec si
 
    cmp byte ptr [si],'.'
    jnz loc_4E6F
    inc si
    test byte ptr ss:614h,1
    jz loc_4E69
    movsb
    mov cx,2
    jmp short loc_4E6C
loc_4E69: mov cx,3
loc_4E6C: call loc_4E83
loc_4E6F: mov al,dl
    and word ptr ss:613h,0FEFFh
 
    cmp byte ptr ss:54Eh,0E5h ; 'х'
    jnz @f
    mov byte ptr ss:54Eh,5
@@: retn
sub_4EB7 endp
table_4EE4:
db 4 dup(66h),6,0Bh dup(66h),0F8h,0F6h,3 dup(0FFh)
db 4Fh,0F4h,6Eh,5 dup(0FFh),44h,44h,0F4h,0Dh dup(0FFh)
db 6Fh,66h,0Fh dup(0FFh),0F4h 
 
sub_4F23 proc ;вызов из двух мест
    lodsb
sub_4F24:: push bx
    mov bx,offset table_0B33
    cmp al,'a'
    jb loc_4F40
    cmp al,'z'
    ja @f
    sub al,20h ;превращаем строчные латинские буквы в прописные
@@: cmp al,80h ;максимальное значение
    jb loc_4F40
    sub al,80h; в AL значения от 0 до 7Fh
    push ds
    mov ds,cs:[new_DS]
    xlat
    pop ds
loc_4F40: push ax
    cmp al,7Eh ; '~'
    jb loc_4F76
    mov al,0Fh
    jmp a7
loc_4F76: push bx
    mov bx,offset table_4EE4
    shr al,1; в AL значения от 0 до 7Fh
    xlat byte ptr cs:[bx]
    pop bx
    jnb loc_4F73
    shr al,4
loc_4F73: and al,0Fh
a7:     test al,1
    pop ax
    pop bx
    retn
sub_4F23 endp
 
sub_4F79 proc ;вызов из 13 мест
    cmp al,'/'
    jbe loc_4F83
    cmp al,'\'
    retn
loc_4F80: mov al,'\'
    retn
loc_4F83: jz loc_4F80
    retn
sub_4F79 endp
 
loc_51F0: cmp byte ptr ss:12FCh,0
    stc
    jnz loc_51FA
    retn
loc_51FA: mov al,ss:12FDh
    push ax
    mov byte ptr ss:34Dh,18h
    call sub_521A
    cmp al,1
    jz loc_5212
    pop ax
    add ax,13h
    stc
    retn
loc_5212: pop ax
 db 0C4h,0C4h ; -
    push ax
    xor si,[bp+si-29h]
    retn
 
sub_521A proc 
    xchg ax,di
    and di,0FFh
    xor ah,ah
    or ah,ss:[34Dh]
    mov al,ss:[12FEh]
    mov word ptr ss:[3B7h],0
    cmp al,0FFh
    jnz loc_523F
    or ah,80h
    or word ptr ss:[3B7h],8000h
loc_523F: mov ss:[584h],es
    mov ss:[582h],bp
    mov si,3B3h
    mov bp,ss
sub_521A endp
 
loc_5424: push ds
    push ax
    mov ax,40h
    mov ds,ax
    test word ptr ds:[314h],2400h
    jnz loc_5482
    push bp
    mov bp,sp
    mov ax,[bp+0Ah]
    pop bp
    test ax, 100h
    jnz loc_5482
    test ax,200h
    jz loc_5466
    lock or word ptr ds:314h,200h
    test word ptr ds:314h,3
    jz loc_5453
loc_5482: pop ax
    pop ds
    iret
return: ret; завершаем программу
loc_5466: lock and word ptr ds:314h, 0FDFFh
loc_5453: xchg ah,al
    cld
    test al, 4
    jz loc_545A
    std
loc_545A: test al,8
    jnz loc_546F
    jo loc_5479
loc_5460: sahf
    pop ax
    pop ds
    retf 2;выход по ошибке
loc_546F: jo loc_5460
    stc
    jmp short loc_5460
loc_5479: clc
    jmp short loc_5460
;------------------------------------
sub_6168 proc 
    xor bx,bx
loc_616A: mov es,cs:[new_DS] 
    mov es,es:332h
    cmp bx,es:32h
    jb @f
    mov al,6;недопустимый дескриптор
    stc
    retn
@@: les di,es:[34h]
    add di,bx
    jb @f
    cmp byte ptr es:[di],0FFh
    jz locret_617A
    inc bx
    jmp short loc_616A
@@: mov al,4;открыто слишком много файлов (нет свободных дескрипторов)
locret_617A: retn
sub_6168 endp
 
sub_613D proc 
    mov es,cs:[new_DS] ;cs:word_2257
    les di,es:2Ah
loc_6147: cmp bx,es:[di+4]
    jb loc_615B
    sub bx,es:[di+4]
    les di,es:[di]
    cmp di,0FFFFh
    jnz loc_6147
    stc
    retn
loc_615B: push ax
    mov ax,33
    mul bl
    add di,ax
    pop ax
    add di,6
    retn
sub_613D endp
 
sub_617B proc 
    push ax
    xor bx,bx
loc_617E: push bx
    call sub_613D
    pop bx
    jb loc_61AE
    cmp word ptr es:[di],0
    jz loc_619E
    cmp word ptr es:[di],0FFFFh
    jz loc_6194
loc_6191: inc bx
    jmp short loc_617E
loc_6194: mov ax,ss:33Eh
    cmp es:[di+1Bh],ax
    jnz loc_6191
loc_619E: mov word ptr es:[di],0FFFFh
    mov ax,ss:33Eh
    mov es:[di+1Bh],ax
    pop ax
    clc
    retn
loc_61AE: pop ax
    mov al,4
    retn
sub_617B endp
sub_6659 proc 
    push ax
    les di,ss:5A4h
    mov word ptr es:[di+43h],0
    cmp al,'Z'
    jna loc_6681
    stc
    jmp short @f
loc_6681: mov ah,':'
    mov es:[di],ax
    mov word ptr es:[di+2],'\'
    or byte ptr es:[di+44h],40h
    mov al,2
    mov es:[di+45h],ax
@@: pop ax
    retn
sub_6659 endp
 
sub_66BA proc 
    call sub_66D7
    jb @f
    push ds
    push si
    lds si,ss:5A4h
    test word ptr [si+43h],2000h
    pop si
    pop ds
    jz @f
    mov byte ptr ss:612h,0Fh
    stc
@@: retn
sub_66BA endp
 
sub_66D7 proc 
    or al,al
    jnz loc_66E0
    mov al,ss:338h
    inc ax
loc_66E0: dec ax
    push ds
    push si
    mov byte ptr ss:325h,2
    test byte ptr ss:575h,0FFh
    jz loc_6712
    push ax
    push es
    push di
    mov word ptr ss:5A4h,507h
    mov ss:5A6h,ss
    add al,'A'
    call sub_6659
    test word ptr es:[di+43h],4000h
    pop di
    pop es
    pop ax
    jz loc_671E
    jmp short loc_672B
loc_6712: call loc_672E
    jb loc_671E
    test word ptr [si+43h],4000h
    jnz loc_672B
loc_671E: mov al,0Fh
    mov ss:612h,al
    mov byte ptr ss:325h,1
    stc
loc_672B: pop si
    pop ds
    retn
sub_66D7 endp
loc_672E: cmp byte ptr ss:13CDh,0
    jz loc_6762
    push bx
    push cx
    lds si,ss:3Ch
    mov cl,ss:47h
    xor ch,ch
loc_6744: or word ptr [si+43h],800h
    mov bx,47h ; 'G'
    add si,bx
    loop loc_6744
    pop cx
    pop bx
    mov byte ptr ss:13CDh,0
    mov si,13F5h
    push ss
    pop ds
    or word ptr [si+43h],0C00h
loc_6762: cmp al,ss:47h
    jb loc_6789
    cmp al,19h
    ja loc_6787
    mov si,13F5h
    push ss
    pop ds
    push ax
db 0C4h,0C4h ; -
    push sp
    add al,58h ; 'X'
    jb loc_6787
    mov word ptr [si+45h],2
    mov word ptr [si+43h],4400h
    jmp short loc_67AC
loc_6787: stc
    retn
loc_6789: push bx
    push ax
    lds si,ss:3Ch
    mov bl,47h ; 'G'
    mul bl
    add si,ax
    pop ax
    pop bx
    test word ptr [si+43h],800h
    jz loc_67AC
    push ax
db 0C4h,0C4h ; -
    push sp
    add al,58h ; 'X'
    jb loc_6787
    and word ptr [si+43h],0F7FFh
loc_67AC: mov ss:5A4h,si
    mov ss:5A6h,ds
    clc
    retn
 
sub_681A proc 
    xor al,al
    jmp short loc_6820
sub_681A endp
sub_681E proc 
    mov al,0FFh
loc_6820:: mov ss:34Eh,al
    mov al,0FFh
    mov ss:574h,al
    mov byte ptr ss:57Dh,0FFh
    mov ss:5B4h,di
    mov word ptr ss:5B8h,0FFFFh
    push ss
    pop es
    lea bp,[di+86h]
    call sub_6ADB
    push ax
    mov ax,[si]
    call sub_4F79
    xchg ah,al
    call sub_4F79
    jnz loc_6868
    cmp ah,al
    jnz loc_6868
    pop ax
    movsw
loc_6858: lodsb
    call sub_4F24
    or al,al
    jz loc_6863
    stosb
    jmp short loc_6858
loc_6863: stosb
    mov ax,0FFFFh
    retn
loc_6868: pop ax
    cmp byte ptr [si],0
    jnz loc_6872
    mov al,2;файл не найден
    stc
    retn
loc_6872: push ax
    push bp
    call loc_4B13
    pop bp
    pop ax
    jnb loc_68A2
    or dx,dx
    jz loc_6883
loc_687F: mov al,3;путь не найден
    stc
    retn
loc_6883: mov byte ptr ss:575h,0FFh
    call sub_66D7
    mov byte ptr ss:575h,0
    jb loc_687F
    call sub_6AF2
    mov al,'/'
    stosb
@@: lodsb
    call sub_4F24
    call sub_4F79
    stosb
    or al,al
    jnz @b
    xor ax,ax
    push ss
    pop ds
locret_68A1: retn
loc_68A2: call sub_66BA
    mov al,3
    jb locret_68A1
    push ds
    push si
    lds si,ss:5A4h
    mov bx,di
    add bx,[si+45h]
    lea bp,[di+86h]
    call sub_3401
    dec di
    mov al,'\'
    cmp es:[di-1],al
    jz loc_68C6
    stosb
loc_68C6: dec di
    pop si
    pop ds
    call sub_6991
    jnz loc_68DF
    or al,al
    jz loc_68E2
    mov di,bx
loc_68D4: lodsb
    call sub_4F79
    jz loc_68D4
    dec si
    or al,al
    jz loc_68E2
loc_68DF: mov al,'\'
    stosb
loc_68E2: call sub_6923
    jb locret_68A1
    push ss
    pop ds
    mov di,ds:5B4h
    lds si,ds:5A4h
    call sub_6AFA
    jnz loc_690A
    mov al,[si-1]
    call sub_4F79
    jz loc_690A
    cmp byte ptr es:[di],0
    jz loc_690A
    inc di
    mov ss:5B8h,di
loc_690A: push ss
    pop ds
    mov si,ds:5B4h
    xor cx,cx
    test byte ptr ds:574h,0FFh
    jz loc_691C
    call sub_6A3C
loc_691C: push ss
    pop ds
    mov ax,0FFFFh
    clc
    retn
sub_681E endp
sub_6923 proc 
    lodsb
    call sub_4F79
    jnz loc_6930
    cmp di,bp
    jnb loc_694F
    stosb
    jmp short sub_6923
loc_6930: dec si
loc_6931: xor ax,ax
    cmp [si],al
    jnz loc_695A
    cmp byte ptr es:[di-1],':'
    jz loc_6947
    cmp byte ptr es:[di-1],'\'
    jnz loc_694C
    jmp short loc_694F
loc_6947: mov al,'\'
    stosb
    mov al,ah
loc_694C: stosb
    clc
    retn
loc_694F: call sub_6B13
    mov al,3;путь не найден
    jz loc_6958
    mov al,2;файл не найден
loc_6958: stc
locret_6959: retn
loc_695A: call sub_69AE
    jb locret_6959
    cmp word ptr es:[di],'.'
    jz loc_696D
    cmp word ptr es:[di],'..'
    jnz loc_6976
    dec di
loc_696D: call sub_699B
    mov al,3
    jb locret_6959
    jmp short loc_6978
loc_6976: add di,cx
loc_6978: call sub_6991
    jnz loc_694F
    lodsb
    call sub_4F79
    jnz loc_6930
    cmp di,bp
    jnb loc_694F
    stosb
loc_6988: lodsb
    call sub_4F79
    jz loc_6988
    dec si
    jmp short loc_6931
sub_6923 endp
 
sub_6991 proc 
    mov al,[si]
sub_6993:: or al,al
    jz @f
    call sub_4F79
@@: retn
sub_6991 endp
sub_699B proc 
    cmp di,bx
    jb loc_69AA
    dec di
    mov al,es:[di]
    call sub_4F79
    jnz sub_699B
    clc
    retn
loc_69AA: mov al,3;путь не найден
    stc
    retn
sub_699B endp
sub_69AE proc 
    sub sp,0Eh
    push ds
    push si
    push es
    push di
    push bp
    mov bp,sp
    mov ah,'.'
    movsb
    cmp [si-1],ah
    jnz loc_69D8
    call sub_6991
    jz loc_69D0
    movsb
    cmp [si-1],ah
    jnz loc_6A22
    call sub_6991
    jnz loc_6A22
loc_69D0: xor al,al
    stosb
    mov [bp+6],si
    jmp short loc_6A1F
loc_69D8: mov si,[bp+6]
    call sub_4EB7
    cmp si,[bp+6]
    jz loc_6A22
    test byte ptr ss:575h,0FFh
    jnz loc_69FB
    and dl,1
    add ss:57Dh,dl
    jg loc_6A22
    jnz loc_69FB
    or dl,dl
    jz loc_6A2A
loc_69FB: mov [bp+6],si
    push ss
    pop ds
    mov si,54Eh
    lea di,[bp+0Ah]
    push di
    movsd;копирую 8 байт (имя файла)
    movsd
@@: cmp byte ptr es:[di-1],' '
    jnz @f
    dec di
    inc cx
    cmp cx,8
    jb @b
@@: cmp word ptr [si],' '
    jnz @f
    cmp byte ptr [si+2],' '
    jz loc_4232
@@: mov al,'.'
    stosb
    movsw;копирую 3 байта (расширение файла)
    movsb
@@: cmp byte ptr es:[di-1],' '
    jnz loc_4232
    dec di
    jmp @b
loc_4232: xor ax,ax
    stosb
    pop di
    call sub_340A;в CX длина строки на которую указывает DI
    dec cx
    add cx,[bp+2]
    cmp cx,[bp+0]
    jnb loc_6A22
    mov si,di
    les di,[bp+2]
    call sub_3401
loc_6A1F: clc
    jmp short loc_6A2D
loc_6A22: stc
    call sub_6B13
    mov al,2
    jnz loc_6A2D
loc_6A2A: stc
    mov al,3;путь не найден
loc_6A2D: pop bp
    pop di
    pop es
    pop si
    pop ds
    lahf
    add sp,0Eh
    call sub_340A;в CX длина строки на которую указывает DI
    dec cx
    sahf
    retn
sub_69AE endp
sub_6A3C proc 
    test byte ptr ss:5Ah,0FFh
    jz loc_6AAE
    push word ptr ss:5A4h
    push word ptr ss:5A6h
    push ds
    push si
    pop di
    pop es
    xor ax,ax
@@: call loc_672E
    jb loc_6AA4
    inc al
    test word ptr [si+43h],2000h
    jz @b
    push di
    call sub_6AFA
    jz loc_6A6B
loc_6A68: pop di
    jmp short @b
loc_6A6B: cmp byte ptr es:[di],0
    jnz loc_6A79
    test byte ptr ss:34Eh,0FFh
    jnz loc_6A68
loc_6A79: mov si,di
    push es
    pop ds
    pop di
    call sub_6AF4
    mov ax,ss:[5B8h]
    or ax,ax
    js loc_6A91
    add ax,di
    sub ax,si
    mov ss:[5B8h],ax
loc_6A91: cmp byte ptr [si],0
    jnz @f
    mov al,'\'
    stosb
@@: call sub_3401
    add sp,4
    or cl,1
    jmp short loc_6AB0
loc_6AA4: pop word ptr ss:5A6h
 pop word ptr ss:5A4h
loc_6AAE: xor cx,cx
loc_6AB0: lds si,ss:5A4h
locret_6AB5: retn
sub_6A3C endp
 
sub_6ADB proc 
    xor al,al
    cmp byte ptr [si],0
    jz @f
    cmp byte ptr [si+1],':'
    jnz @f
    lodsw
    or al,20h
    sub al,60h ; '`'
    jnz @f
    mov al,0FFh
@@: retn
sub_6ADB endp
 
sub_6AF2 proc
    inc al
sub_6AF4:: add al,40h ; '@'
    mov ah,3Ah ; ':'
    stosw
    retn
sub_6AF2 endp
 
sub_6AFA proc 
    push bx;в CX длина строки на которую указывает SI 
    xor bx,bx
@@: cmp byte ptr [si+bx],0
    jz @f
    inc bx
    jmp @b
@@: lea cx,[bx-1]; cx := bx - 1
    pop bx
    repe cmpsb
    jnz a1
    push ax
    mov al,[si-1]
    call sub_4F79
    jz @f
    mov al,es:[di]
    call sub_6993
@@: pop ax
a1: retn
sub_6AFA endp
 
sub_6B13 proc 
    lodsb
    call sub_6993
    jnz sub_6B13
    call sub_4F79
    retn
sub_6B13 endp
 
 
sub_6CA9: ;proc
    push cx
    mov cx,4652h
    mov byte ptr ss:570h,6
loc_6B28: call sub_617B
    jb loc_6C90
    mov word ptr es:[di+5],0
    mov ss:5ACh,bx
    mov ss:5A0h,di
    mov ss:5A2h,es
    call sub_6168
;   jnb loc_6B4A
;loc_6B47: jmp loc_6C90
loc_6B4A: mov ss:5B0h,di
    mov ss:5B2h,es
    mov ss:5AEh,bx
    mov bx,ss:5ACh
    mov es:[di],bl
    mov si,offset filename
    mov di,3D2h
    push cx
    call sub_681A
    mov dx,ax
    pop bx
    lds si,ss:5A0h
    jb loc_6C81
    cmp byte ptr ss:57Dh,0FFh
    jnz loc_6C81
loc_6B81: pop ax
    xor cx,cx
    mov [si+2],cx
    cmp bx,45DFh
    jnz loc_6B96
    test al,80h
    jz loc_6B96
    and al,7Fh
    mov cx,1000h
loc_6B96: push di
    push es
    push ds
    pop es
    push si
    pop di
    call sub_6FB1
    pop es
    pop di
    push ss
    pop ds
    or dx,dx
    jnz loc_6BAA
loc_6BAA: push bx
    push cx
    push dx
    mov si,ds:5B4h
    push bp
    xor dx,dx
    mov cx,ax
db 0C4h,0C4h;les ax,sp
    push ax
    add si,[bp+di+3]
    call loc_51F0
    mov di,bp
    pop bp
    lds si,ds:5A0h
    jb loc_6C38
    mov [si+1Dh],di
    mov [si+1Fh],ax
    mov dword ptr [si+13h],0
    mov [si+0Fh],cx
    mov [si+11h],bx
    or dx,dx
    jz @f
    or word ptr [si+5],2000h
@@: pop dx
    pop cx
    pop bx
    push ds
    push ss
    pop ds
    mov di,ds:[5B4h]
    cmp byte ptr [di],'A'
    jb loc_6C2F
    cmp byte ptr [di],'Z'
    ja loc_6C2F
    mov al,[di]
    sub al,'A'
    xor ah,ah
    pop ds
    or [si+5],ax
    jmp short @f
loc_6C2F: pop ds
@@: and word ptr [si+5],0FD7Fh
    mov word ptr [si],1
    or [si+5],cx
    mov ax,ss:5AEh
    mov word ptr ss:5ACh,0FFFFh
    call sub_2742
    and word ptr [si+16h],0FFFEh
    jmp loc_2826
loc_6C90: sti
    pop cx
loc_6C94: mov word ptr ss:[5ACh],0FFFFh
    cmp word ptr ss:[326h],25h ; '%'
    jz a2
    xor ah,ah
    push si
    push ds
    push cx
    push bx
    push ss
    pop ds
    mov ds:[326h],ax
    mov si,225Bh
    mov bh,al
    mov bl,ds:[33Dh]
@@: lods word ptr cs:[si]
    cmp al,0FFh
    jz loc_28AB
    cmp al,bl
    jz loc_28AF
    shr ax,8
    add si,ax
    jmp @b
loc_28AB: mov al,bh
    jmp short loc_28BB
loc_28AF: mov cl,ah
    xor ch,ch
@@: lods byte ptr cs:[si]
    cmp al,bh
    jz loc_28BB
    loop @b
loc_28BB: xor ah,ah
    pop bx
    pop cx
    pop ds
    mov si,0DDBh
    cmp byte ptr ss:[34Ch],0
    jz @f
    mov word ptr ss:[326h],53h ; 'S'
@@: push ds
    push ax
    push bx
    mov ds,cs:[new_DS]
    mov bx,ds:[326h]
@@: lodsb
    cmp al,0FFh
    jz @f
    cmp al,bl
    jz @f
    add si,3
    jmp short @b
@@: lodsw
    cmp ah,0FFh
    jz @f
    mov ds:328h,ah
@@: cmp al,0FFh
    jz @f
    mov ds:329h,al
@@: lodsb
    cmp al,0FFh
    jz @f
    mov ds:325h,al
@@: pop bx
    pop ax
    pop ds
    pop si
a2: call sub_2742
    or word ptr [si+16h],1
    stc
loc_2826: mov [si],ax
    retn
 
loc_6C38: pop dx
    pop cx
loc_6C81: pop bx
    mov word ptr [si],0
    lds si,ss:5B0h
    mov byte ptr [si],0FFh
    jmp loc_6C94
 
sub_6FB1 proc 
    test byte ptr ss:5F8h,1
    jz @f
    push ax
    mov ax,ss:603h
    or es:[di+2],ax
    pop ax
    stc
@@: retn
sub_6FB1 endp
;---------------------------------------
filename db 'myfile.txt',0
new_DS dw 0A7h
table_0B33:
db 0Ah, 0E8h, 5Eh, 45h, 0B0h, 82h, 0FEh, 0C4h, 0E8h, 57h, 45h
db 0FEh, 0C4h, 0E8h, 3Bh, 45h, 0FEh, 0C4h, 0E8h, 36h, 45h, 0C3h
db 60h, 0B4h, 0Ah, 0B3h, 0, 0B7h, 80h, 0B9h, 58h, 0, 9Ch, 0FAh 
db 0E8h, 14h, 9, 74h, 4, 9Dh, 0F9h, 0EBh, 2, 9Dh, 0F8h, 61h, 0C3h 
db 3 dup(0), 10h, 3 dup(3 dup(0),1,2 dup(0),10h), 3 dup(0), 1
db 7 dup(0), 80h, 96h, 98h, 0, 40h, 42h, 0Fh, 0, 0A0h, 86h, 1
db 0, 10h, 27h, 2 dup(0), 0E8h, 3, 2 dup(0), 64h, 3 dup(0), 0Ah
db 3 dup(0), 1, 7 dup(0), 3 dup (19h, 0F9h), 4, 0F1h, 4Eh
 
end start
2
Ушел с форума
Автор FAQ
15840 / 7422 / 994
Регистрация: 11.11.2010
Сообщений: 13,385
25.06.2013, 13:20  [ТС] 16
Поиск файлов и вывод содержимого каталога на экран. DOS
Поиск файлов в DOS реализуется, как правило, через функции 4Eh и 4Fh прерывания 21h. Эти функции позволяют обнаруживать в заданном каталоге или на заданном устройстве файлы с именами, соответствующими заданному образцу.
В DOS существует возможность обращаться к группе файлов, задав так называемую маску имени. При задании маски используются обозначения: ? - любой символ, * - любая группа символов. Например:
  • *.* - все файлы
  • *.СОМ - все файлы с расширением .СОМ
  • ?С.ЕХЕ - под ? понимается любая ОДНА буква, таким образом BC.EXE и NC.EXE удовлетворяют условию, а TCC.EXE - нет.
Функции 4h и 4Fh помещают результат своей работы в область передачи данных (DTA), организованную следующим образом:
Байты Содержимое
0 - 14h Зарезервировано
15h Атрибут
16h - 17h Время и дата файла
1Ah - 1Dh Размер файла
1Eh - 2Ah Имя файла
По умолчанию область DTA размещается в программе по адресу DS:80h, то есть там же, где и параметры командной строки. Для того, чтобы программа могла одновременно пользоваться и командной строкой и функциями поиска, часто применяется функция 1Ah прерывания 21h, предназначенная для перемещения области DTA в другое место памяти.
Приведем формат вызова функций поиска:
Код
AH:=4Eh       или          4Fh
    CX:=Атрибут файла
    DS:DX:= Адрес строки маски имени
Атрибут файла - это байт, определяющий внутренние характеристики файла:
БитНазваниеПереводЗначение
0 Read only Только для чтения в этот файл нельзя писать и его нельзя удалить
1 HiddenСкрытыйфайл скрывается от показа, пока явно не указано обратно
2 System Системный содержание файла критично для работы операционной системы
3 Label Метка тома  
4 Directory Каталогфайл, содержащий записи о входящих в него файлах. Каталоги могут содержать записи о других каталогах, образуя древовидную структуру
5 Archive Архивныйфайл изменен после резервного копирования или не был скопирован программами резервного копирования
6 Не используется 
7 Не используется 
8SharedРазделяемый (Novell NetWare)возможность одновременной работы с файлом в локальной вычислительной сети нескольким пользователям одновременно
Если в байте атрибутов установить бит 4 в 1, то функции поиска будут искать каталоги (которые с точки зрения DOS также являются файлами). При установленном бите 1 функции поиска обнаружат скрытые файлы, например в корневом каталоге диска С: файлы MSDOS.SYS и IO.SYS. Для поиска обычных файлов достаточно просто заказать нулевое значение байта атрибутов.
Приведем пример программы, ищущей и отображающей на экране имена всех файлов с расширением .СОМ в текущем каталоге:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
; masm dos com #
.286
.model tiny
.code
org    80h
; Область передачи данных по умолчанию
dta    db     15h dup (0)
attrib db     0
timdat dw     0
       dw     0
fsize  dd     0
fname  db     0Eh dup (0)
       org    100h
; Начало программы
start: mov    ah,4Eh; Ищем 1-й файл
       xor    cx,cx
       mov    dx,offset maska
       int    21h
       jc     net
povtor:mov    si,offset fname
pechat:lodsb; Печатаем имя
       test   al,al
       je     poisk
       int    29h
       jmp    pechat
poisk: mov    al,0Dh; Переводим строку
       int    29h
       mov    al,0Ah
       int    29h
       mov    ah,4Fh; Ищем следующий файл
       int    21h
       jnc    povtor
net:   ret    ; Файлов больше нет, конец
maska  db     '*.com',0; Маска для поиска
end start

Поиск файлов и вывод содержимого каталога на экран.
Используем системную команду DIR
Вывод списка файлов и подкаталогов из указанного каталога.
Код
DIR [диск:][путь][имя_файла] [/A[[:]атрибуты]] [/B] [/C] [/D] [/L] [/N]
  [/O[[:]порядок]] [/P] [/Q] [/S] [/T[[:]время]] [/W] [/X] [/4]
где [диск:][путь][имя_файла] Диск, каталог и/или файлы, которые следует включить в список.
/A Вывод файлов с указанными атрибутами.
 атрибуты
D Каталоги
R Доступные только для чтения
H Скрытые файлы
A Файлы для архивирования
S Системные файлы
  Префикс "-" имеет значение НЕ
/B Вывод только имен файлов.
/C Применение разделителя групп разрядов для вывода размеров файлов (по умолчанию). Для отключения этого режима служит ключ /-C.
/D Вывод списка в несколько столбцов с сортировкой по столбцам.
/L Использование нижнего регистра для имен файлов.
/N Отображение имен файлов в крайнем правом столбце.
/O Сортировка списка отображаемых файлов.
 порядок
N По имени (алфавитная)
S По размеру (сперва меньшие)
E По расширению (алфавитная)
D По дате (сперва более старые)
G Начать список с каталогов
Префикс "-" обращает порядок
/P Пауза после заполнения каждого экрана.
/Q Вывод сведений о владельце файла.
/S Вывод списка файлов из указанного каталога и его подкаталогов.
/T Выбор поля времени для отображения и сортировки
 время
C Создание
A Последнее использование
W Последнее изменение
/W Вывод списка в несколько столбцов.
/X Отображение коротких имен для файлов, чьи имена не соответствуют стандарту 8.3. Формат аналогичен выводу с ключом /N, но короткие имена файлов выводятся слева от длинных. Если короткого имени у файла нет, вместо него выводятся пробелы.
/4Вывод номера года в четырехзначном формате
Стандартный набор ключей можно записать в переменную среды DIRCMD. Для отмены
их действия введите в команде те же ключи с префиксом "-", например: /-W.
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
; masm dos com #
.286
.model tiny
.code
org 100h
start:  mov ah,4Ah  ;allow mem alloc.
    mov bx,10h  ;новый размер блока в параграфах
    int 21h     
    mov si,offset command_line
    int 2Eh ; execute a command using BASE LEVEL
    mov ah,4Ch  ;выходим из программы
    int 21h
command_line db N,'DIR',0Dh
N = $ - command_line - 1
end  start
или так
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
; masm dos com #
.286
.model tiny
.code
org 100h
start:  mov si,offset string    
    mov cx,N
@@: push cx
    lodsb
    mov ah,5      ;номер функции
    mov cl,al;передача параметра через регистр CL, содержимое CH=0 
    int 16h
    pop cx
    loop @b
    retn
string db 'DIR',0Dh; не более 15 символов
N = $ - string
end start
или так
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
; masm dos com #
.286
.model tiny
.code
org 100h
start:  mov bx,100h     ;выделим блок памяти в 256 параграфов
        mov ah,4Ah               
        int 21h
        mov bx,offset parametrs ;указываем на блок параметров
        mov [bx+4],cs
        mov dx,offset filename
        mov ax,4B00h;загрузить и выполнить программу из командной строки
        int 21h
        retn        ;выход в DOS
command_line db N,'/c DIR',0Dh
N = $-command_line-1;длина командной строки
;командная строка типа pascal, начинается с байта длины строки, заканчивается
;ASCII-кодом клавиши Enter (0Dh). При передаче команды CMD.EXE нужно указать /С перед 
;строкой (требование вызова вторичного командного процессора). Программу cmd.exe
;из папки windows\system32\ проще разместить в том же каталоге, что и программа 
filename db 'cmd.exe',0
parametrs dw 0,command_line,5 dup(0);блок параметров
end start
или вот так
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
; masm dos com #
.286
.model tiny
.code
org     100h
start:  mov ah,4Ah      ;модифицируем назначенную память
        mov bx,100h     ;новый размер блока в параграфах
        int 21h         
        or bx,-1        ;требуемое число параметров для выполнения
        mov ah,48h      ;вделить блок памяти
        int 21h         ;возвращает в bx размер наибольшего доступного блока 
;памяти в параграфах
        mov ah,48h      ;вделить блок памяти, в bx действительно доступная память
        int 21h         ;возвращает в ах сегментный адрес выделенного блока
        mov es,ax       ;es:=new PSP
        mov ss,ax       ;ss:=new PSP
        xchg dx,ax      ;dx:=new PSP
        mov ah,26h      ;создать новый префикс программного сегмента
        int 21h         
        mov dx,offset filename
        mov di,80h      ;указатель на командную строку
        mov cx,(N+1)/2  ;копируем по два байта за раз, N округлен в большую 
;сторону к числу кратному два, поэтому добавочный movsb не нужен
        mov si,offset command_line
        rep movsw       ;создаем командную строку для запуска command.com
        mov ax,3D00h    ;открыть command.com на чтение
        int 21h
        xchg bx,ax      ;дескриптор файла в bx
        xor cx,cx       ;cx=dx=0
        xor dx,dx
        mov ax,4202h    ;установить файловый указатель на конец файла
        int 21h
        push ax         ;сохраним в стеке длину файла command.com
        mov ax,4200h    ;установить файловый указатель на начало файла
        int 21h
        mov dx,es       ;пересчитываем чему должен быть равен dx из расчета,
        add dx,10h      ;что cs*10h+dx=es*10h+100h
        mov cx,cs
        sub dx,cx
        shl dx,4        ;в dx адрес буфера, куда будет скопирован command.com
        pop cx          ;в сх число байтов для чтения 
        mov ah,3Fh      ;читаем command.com в буфер
        int 21h
        mov ah,3Eh      ;закрыть файл command.com
        int 21h
        mov ax,es       ;ds:=new PSP
        mov ds,ax       ;при старте СОМ-файла cs=ds=es=ss=PSP ip=100h
        push es         ;новое значение cs:=new PSP
        push 100h       ;новое значение ip:=100h
        retf            ;запускаем command.com для создания файла myfile.txt
filename db 'c:\windows\system32\command.com',0;полный путь к command.com
command_line db N-1,'/c DIR',0Dh
N = $ - command_line
db 0; если N нечетное, то копируется и добавочный нулевой символ 
end     start

Поиск файлов и вывод содержимого каталога на экран. Windows
Пример из книги Пирогова "Ассемблер для Windows". Программа осуществляет поиск в указанном или текущем каталоге. Поиск проходит по дереву каталогов, начиная с заданного
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
; masm windows console #
.686
.model flat
include \masm32\include\windows.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
;объявление используемых функций Windows (внешние ссылки)
extern _imp__wsprintfA:dword
extern _imp__CharToOemA@8:dword;
extern _imp__GetStdHandle@4:dword;
extern _imp__WriteConsoleA@20:dword;
extern _imp__ReadConsoleA@20:dword;
extern _imp__ExitProcess@4:dword;
extern _imp__GetCommandLineA@0:dword;
extern _imp__lstrcat@8:dword;
extern _imp__lstrcpyA@8:dword;
extern _imp__FindFirstFileA@8:dword;
extern _imp__FindNextFileA@8:dword;
extern _imp__FindClose@4:dword;
extern _imp__lstrlenA@4:dword
_FIND STRUC;структура, используемая для поиска файлов при помощи
;функций FindFirstFileA и FindNextFileA
ATR     dd ?;атрибут файла
CRTIME  dd ?;время создания файла
        dd ?
ACTIME  dd ?;время доступа к файлу
    dd ?    
WRTIME  dd ?;время модификации файла
    dd ?
SIZEH   dd ?;размер файла
SIZEL     dd ?
    dd ?;резерв
    dd ?
NAM db 260 dup(0);длинное имя файла
ANAM db 14 dup(0);короткое имя файла
_FIND ENDS
.code
start:   push STD_INPUT_HANDLE
         call _imp__GetStdHandle@4;получаем HANDL1 для ввода
         mov HANDL1,eax
         push STD_OUTPUT_HANDLE
         call _imp__GetStdHandle@4;получаем HANDL для вывода
         mov HANDL,eax
         call NUMPAR ;получить количество параметров
     mov PAR,eax
     dec eax;если параметр один, то искать в текущем каталоге
     je NO_PAR
     mov edx,2;получить параметр номером edi
     lea ebx,BUF
     call GETPAR
     jb NO_PAR
         mov edi,3;подключить параметр - маску поиска
     lea ebx,MASKA
     call GETPAR
NO_PAR:  push offset BUFER
     call FIND
     push NUMF;вывести количество файлов
     push offset FORM
     push offset BUFER
     call _imp__wsprintfA
         add esp,12
     lea eax,BUFER
     mov edi,1
     call WRITE
     push NUMD;вывести количество каталогов
     push offset FORM1
     push offset BUFER
     call _imp__wsprintfA
         add esp,12
     lea eax,BUFER
     mov edi,1
     call WRITE
_END:    push 0
         call _imp__ExitProcess@4
;---------------------------------------------------------------
WRITE PROC;вывести строку с переводом строки
     push eax
         push eax
     call _imp__lstrlenA@4;получить длину параметра
     pop ebx
     mov dword ptr [ebx+eax],0D0Ah
     add eax,2
NO_ENT:  push 0;резерв
     push offset LENS;выведено символов
     push eax;длина строки
     push ebx;адрес строки
     push HANDL;HANDL вывода
         call _imp__WriteConsoleA@20;вывести строку 
         RET
WRITE ENDP
;-------------------------------------------------
NUMPAR PROC;определение параметров в командной строке
    call _imp__GetCommandLineA@0
    mov esi,eax
    xor ecx,ecx
    mov edx,1
L1: cmp byte ptr [esi],0
    je L4
        cmp byte ptr [esi],32
    je L3
    add ecx,edx;номер параметра
        xor edx,edx
    jmp L2
L3: or edx,1
L2: inc esi
    jmp L1
L4: mov eax,ecx
    ret
NUMPAR ENDP
;------------------------------------------------------
GETPAR PROC;в edi указатель на буфер, куда помещен параметр
;в ebx номер параметра
        call _imp__GetCommandLineA@0
    mov esi,eax
    xor ecx,ecx
    mov edx,1
L10:    cmp byte ptr [esi],0
    je L40
        cmp byte ptr [esi],32
    je L30
    add ecx,edx;номер параметра
        xor edx,edx
    jmp L20
L30:    or edx,1
L20:    cmp ecx,edi
    jne L50
        mov al,[esi]
    mov [ebx],al
    inc ebx
L50:    inc esi
    jmp L10
L40:    mov byte ptr [ebx],0
    ret
GETPAR ENDP
FIND PROC;поиск в каталоге файлов и вывод имени каталога в BUF
FINDH   equ [ebp-4]
DIRS    equ [ebp-304]
DIRSS   equ [ebp-604]
DIRV    equ [ebp-904]
DIR equ [ebp+8]
    enter 904,0
    xor eax,eax
    lea edi,DIRV
    mov ecx,900/4
    rep stosd
    push DIR
    call _imp__lstrlenA@4
    mov ebx,eax
    mov edi,DIR
    cmp byte ptr [edi],0
    je _OK
    cmp byte ptr [edi+ebx-1],'\'
    je _OK
        push offset AP
    push DIR
    call _imp__lstrcat@8
_OK:    push DIR
    lea eax,DIRSS
    push eax
    call _imp__lstrcpyA@8
    push offset MASKA;путь с маской
    push DIR
    call _imp__lstrcat@8
        push offset FIN;здесь начало поиска
    push DIR
    call _imp__FindFirstFileA@8
    inc eax;cmp eax,-1
    je _ERR
    dec eax
    mov FINDH,eax;сохранить дескриптор поиска
LF: cmp byte ptr FIN.NAM,".";исключить файлы "." и ".."
    je _FF
    lea eax,DIRSS
    push eax
    lea eax,DIRS
    push eax
    call _imp__lstrcpyA@8
        push offset FIN.NAM
    lea eax,DIRS
        push eax
    call _imp__lstrcat@8
    test byte ptr FIN.ATR,10h;не каталог ли?
    je NO_DIR
    push offset DIRN
    lea eax,DIRS
        push eax
    call _imp__lstrcat@8
        inc NUMD
    dec NUMF
    mov PRIZN,1
    lea eax,DIRS
    push eax
    call OUTF
    jmp _NO
NO_DIR: lea eax,DIRS
    push eax
    call OUTF
    mov PRIZN,0
_NO:    cmp PRIZN,0
        jz _F
        lea eax,DIRSS
    push eax
    lea eax,DIRV
    push eax
    call _imp__lstrcpyA@8
    push offset FIN.NAM
    lea eax,DIRV
    push eax
    call _imp__lstrcat@8
    lea eax,DIRV
    push eax
    call FIND
_F: inc NUMF
_FF:    push offset FIN
    push FINDH
    call _imp__FindNextFileA@8;продолжение поиска
    test eax,eax;cmp eax,0
    jne LF
    push FINDH
    call _imp__FindClose@4;закрыть поиск
_ERR:   leave
    ret 4
FIND ENDP
OUTF proc
STRN    equ [ebp+8]
    push ebp
    mov ebp,esp
    push STRN
    push STRN
    call _imp__CharToOemA@8;преобразовать строку
    mov eax,STRN
    mov edi,1
    call WRITE;здесь вывод результата
    inc NUM
    cmp NUM,22;конец страницы?
    jne NO
    mov NUM,0
    mov edi,1
    lea eax,TEXT
    call WRITE;ждать вывод строки
    push 0
    push offset LENS
    push 10
    push offset BUFIN
    push HANDL1
    call _imp__ReadConsoleA@20
NO: pop ebp
    ret 4
OUTF endp
;data------------------------------------------
BUF     db 0
    db 100 dup (?)
LENS    dd 100;количество выведенных символов
HANDL   dd ?
HANDL1  dd ?
MASKA   db "*.*",0
AP  db "\",0
FIN _FIND <0>
TEXT    db '„«п Їа®¤®«¦Ґ*Ёп **¦¬ЁвҐ Є«*ўЁиг ENTER',13,10,0;"Нажмите клавишу Enter" в кодировке CP-866
BUFIN   db 10 dup(0)
NUM db 0
NUMF    dd 0;счетчик файлов
NUMD    dd 0;счетчик каталогов
FORM    db '—Ёб«® **©¤Ґ*ле д*©«®ў: %lu',0;"Число найденных файлов"
FORM1   db '—Ёб«® **©¤Ґ*ле Є*в*«®Ј®ў: %lu',0;"Число найденных каталогов"
BUFER   db 100 dup(0)
DIRN    db " <DIR>",0
PAR     dd 0;количество параметров
PRIZN   db 0      
end start
6
Ушел с форума
Автор FAQ
15840 / 7422 / 994
Регистрация: 11.11.2010
Сообщений: 13,385
25.06.2013, 13:20  [ТС] 17
ОСНОВНЫЕ ПРАВИЛА НАПИСАНИЯ ПРОГРАММ НА ЯЗЫКЕ АССЕМБЛЕРА
Данные правила относятся не только к программированию на языке ассемблера, но и к программированию на других языках. Может быть, их трудно понять, не имея навыка в программировании, но «незнание основ не освобождает от ответственности».
Начинайте с комментариев
Начните с написания инструкции для пользователя — для чего создается и каковы возможности вашей программы. А теперь немного усложните вашу инструкцию по применению вашей программы, подразумевая под «пользователем» программиста, использующего написанный вами код — зачастую этим программистом-исследователем будете вы сами.
Акт записи на обычном языке описания того, что делает программа и что делает каждая функция в программе, является критическим шагом в мыслительном процессе. Хорошо построенное, грамматически правильное предложение — признак ясного мышления. Если вы не можете это записать, то велика вероятность того, что вы не полностью продумали задачу или метод ее решения. Плохая грамматика и построение предложения являются также показателем поверхностного мышления. Поэтому первый шаг в написании любой программы — записать, что именно и как делает программа.
Итак, комментарии для вашей программы уже готовы. Теперь возьмите ваше описание по использованию и добавьте вслед за каждым абзацем блоки кода, реализующие функции, описанные в этом абзаце. Оправдание: «У меня не было времени, чтобы добавить комментарии» на самом деле означает — «Я писал этот код без проекта системы и у меня нет времени воспроизвести его». Если создатель программы не может воспроизвести идеи, воплощенные в программный проект, то кто же тогда сможет?
Работа программиста состоит из двух частей: разработать приложение для пользователя и сделать возможным дальнейшее сопровождение программы. Единственный способ решить вторую часть задачи — комментировать код. Причем комментарии должны описывать не только, что делает код, но и предположения, принятый подход и причины, по которым вы выбрали именно его. Кроме того, необходимо, чтобы комментарии также соответствовали коду. Пишите код так, словно тот, кто будет заниматься его поддержкой, — опасный психопат, знающий где вы живете. Хотя вы можете считать, что ваш код полностью очевиден и может служить примером ясности, без правильных комментариев понять его постороннему достаточно трудно. Парадокс заключается в том, что спустя неделю вы сами можете оказаться в роли этого постороннего.
Старайтесь использовать следующий подход при написании комментариев:
  • перед каждой функцией или методом размещается одно или два предложения со следующей информацией:
    • что делает программа;
    • возникающие при этом предположения о программе;
    • что должно содержаться во входных параметрах;
    • что должно содержаться во выходном параметре в случае успешного или неудачного завершения;
    • все возможные выходные значения;
  • перед каждой не совсем очевидной частью функции следует поместить одно или два предложения, объясняющие выполняемые действия;
  • любой интересный алгоритм заслуживает подробного описания;
  • любая нетривиальная ошибка, устраненная в коде, должна комментироваться, при этом нужно привести номер ошибки и описать сделанное исправление;
  • правильно размещенные операторы диагностики, проверки условий, а также соглашения об именах переменных могут также служить хорошими комментариями и передавать содержание кода;
  • писать комментарии так, будто сами собираетесь заниматься его поддержкой через пять лет;
  • если возникла мысль «это хитро сделано» или «это ловкий трюк» — лучше переписать данную функцию, а не комментировать ее.
Если вы будете правильно писать комментарии – вы в безопасности, даже если программист из службы поддержки окажется психопатом.
Читайте код
Все писатели — это читатели. Вы учитесь, когда смотрите, что делают другие писатели. Я настоятельно рекомендую, чтобы, как минимум, члены группы программирования читали код друг у друга. Читатель может найти ошибки, которые вы не увидели, и подать мысль, как улучшить код. Для вас лучше присесть с коллегой и просто разобрать код строка за строкой, объясняя, что и как делается, получить какую-то обратную связь и совет. Для того чтобы подобное упражнение принесло пользу, автор кода не должен делать никаких предварительных пояснений. Читатель должен быть способен понимать код в процессе чтения. Если вам пришлось объяснять что-то вашему читателю, то это значит, что ваше объяснение должно быть в коде в качестве комментария. Добавьте этот комментарий, как только Вы его произнесли; не откладывайте этого до окончания просмотра.
Разлагайте сложные проблемы на задачи меньшего размера
На самом деле это также и правило литературного стиля. Если очень трудно объяснить точку зрения за один раз, то разбейте изложение на меньшие части и по очереди объясняйте каждую. То же самое назначение у глав в книге и параграфов в главе.
Используйте язык полностью
Некоторые программисты считают одним из недостатков языка ассемблера большее, по сравнению с языками высокого уровня, количество команд, но, по-моему, это одно из достоинств языка ассемблера.
Проблема должна быть хорошо продумана перед тем, как она сможет быть решена
Это относится не только к программированию на языке ассемблера.
Отредактируйте свой код. Программа должна писаться не менее двух раз
Раньше, когда вы изучали в школе литературу, вам никогда не приходило в голову сдавать черновик письменного задания, если Вы, конечно, рассчитывали на оценку выше тройки. Тем не менее, многие компьютерные программы являются просто черновиками и содержат столько же ошибок, сколько и черновики ваших сочинений. Хороший код программы должен быть сначала написан, а затем отредактирован в целях улучшения (под «редактированием» имеется в виду «исправление»). Редактирование, как правило, приводит к сокращению кода, а небольшие программы выполняются быстрее.
Оптимизация программ на языке ассемблера
Итак ваша программа заработала, а теперь постарайтесь переделать ее так, чтобы она стала максимально компактной и в тоже время максимально быстродействующей.
Такая оптимизация достигается в три этапа:
  • Алгоритмическая оптимизация то есть подбор алгоритма, который выполняет вашу задачу более быстрым способом и позволит сократить не пять, а пятьдесят операторов;
  • Подстройка программы под конкретное оборудование;
  • Замена некоторых ассемблерных команд на машинный код. Тщательный анализ машинного кода, вырабатываемого транслятором, позволяют прийти к выводу, что некоторые коды человек может выработать более оптимально, чем программа.

Для написания этого топика были использованы статьи из следующих книг
  1. Ален И. Голуб «Правила программирования С & С++» — М. : БИНОМ, 1996. — 272 с.
  2. Джон Роббинс «Отладка Windows-приложений» — М.: ДМК Пресс, 2001. — 448 с., ил.
4
Ушел с форума
Автор FAQ
15840 / 7422 / 994
Регистрация: 11.11.2010
Сообщений: 13,385
25.06.2013, 15:13  [ТС] 18
Графика DOS. Двигаемся в тоннеле
Tunnel - 256 bytes
01-11-2002
256 bytes intro: realtime rendering, software rendering, MsDos, x86 real mode, assembler, demoscene
Автор Iсigo Quñlez, взято здесь и немного доработано
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36