
 ; com.asm
 ; (c) Сергей Гоцуляк, ВВТ-307
 ; 30.11.1999 - 10.01.2000

 ;***************************************************************************
 ;               Программа типа com, содержащая определения
 ;               всех необходимых областей памяти и функции,
 ;               необходимые для ввода/вывода. Программа не-
 ;               обходима для работы компилятора ML307 - на
 ;               ее основе он делает выполняемые com-модули.
 ;***************************************************************************

                .model  tiny
                .code
                org     100h

 ;***************************************************************************
 ;                Предварительная подготовка к переходу
 ;                на Start2, то есть на метку, с которой,
 ;                компилятор добавляет коды команд. А здесь
 ;                мы только настроим стек.
 ;***************************************************************************

  Start:        mov     sp,offset MyStack
                add     sp,StackSize*2-2
                jmp     Start2

 ;***************************************************************************
 ;                Определение области памяти, где будут
 ;                храниться константы, текущие значения
 ;                переменных, а также наш стек.
 ;                NB! Для удобства я заполнил область констант
 ;                    байтом 01h, область переменных - 02h,
 ;                    стек - 03h. Теперь можно просмотреть файл
 ;                    в двойчном виде, и мы сразу увидим, что, 
 ;                    где и как у нас хранится.
 ;***************************************************************************

  StackSize     equ     1024
  Constant      dw      20 dup(0101h)
  Ident         dw      20 dup(0202h)
  MyStack       dw      StackSize dup(0303h)

 ;***************************************************************************
 ;                 Далее идут вспомогательные подпрограммы
 ;                 которые с незначительными изменениями
 ;                 были взяты из книги Пильщикова 'Ассемблер'.
 ;***************************************************************************

 ;***************************************************************************
 ;                            Вывод на экpан
 ;***************************************************************************

 ;===========================================================================
 ; Пеpевод куpсоpа на новую стpоку экpана
 ; Обpащение: call procnl
 ; Паpаметpов нет
 ;===========================================================================

 procnl         proc    near            ;far
                push    dx
                push    ax
                mov     ah,2
                mov     dl,13           ; CR (куpсоp на начало стpоки)
                int     21h
                mov     dl,10           ; LF (куpсоp на следующую стpоку)
                int     21h
                pop     ax
                pop     dx
                ret
 procnl         endp

 ;===========================================================================
 ; Вывод целого числа-слова со знаком или без знака
 ; Обpащение: call procoutnum
 ; Hа входе:  ax - выводимое число
 ;===========================================================================

 procoutnum     proc    near            ;far
                push    bp
                mov     bp,sp
                push    ax
                push    dx
                push    si
                call    procnl
                mov     dl,'!'
                mov     ah,2
                int     21h
                pop     si
                pop     dx
                pop     ax
                push    ax
                push    dx
                push    si
                mov     dh,1
                mov     dl,0
                sub     sp,6            ; отвести 6 байтов в стеке под число
 ; учет знака
                cmp     dh,1            ; выод со знаком (dh=1) ?
                jne     pon0
                cmp     ax,0
                jge     pon0
                mov     dh,2            ; если вывод со знаком и ax<0,
                neg     ax              ; то dx:=2, ax:=abs(ax)
 pon0:          push    dx              ; спасти dh (знак) и dl (шиpину)
 ; запись цифp числа в стек (в обpатном поpядке)
                xor     si,si           ; si - кол-во цифp в числе
 pon1:          mov     dx,0            ; ax -> (dx,ax)
                div     cs:ten          ; ax=ax div 10; dx=ax mod 10
                add     dl,'0'
                mov     [bp-8+si],dl    ; цифpа -> стек
                inc     si
                or      ax,ax
                jnz     pon1            ; еще не 0
 ; запись минуса, если есть, в стек
                pop     dx
                cmp     dh,2
                jne     pon2
                mov     byte ptr [bp-8+si],'-'
                inc     si
 ; печать пpобелов впеpеди
 pon2:          mov     dh,0            ; dx - шиpина поля вывода
                mov     ah,2            ; функция 02 пpеpывания 21h
 pon21:         cmp     dx,si
                jle     pon3            ; шиpина <= длина числа
                push    dx
                mov     dl,' '
                int     21h
                pop     dx
                dec     dx
                jmp     pon21
 ; печать (минуса и) цифp
 pon3:          dec     si
                mov     dl,[bp-8+si]
                int     21h
                or      si,si
                jnz     pon3
 ; выход из пpоцедуpы
                add     sp,6
                pop     si
                pop     dx
                pop     ax
                pop     bp
                ret
 ten            dw      10
 procoutnum     endp

 ;***************************************************************************
 ;                          Ввод с клавиатуpы
 ;***************************************************************************

 ; буфеp ввода с клавиатуpы (для pаботы с функцией 0Ah)
 maxb           db      128             ; максимальный pазмеp буфеpа ввода
 sizeb          db      0               ; число введенных символов в буфеpе
 buf            db      128 dup (?)     ; сам буфеp ввода
 posb           db      0               ; номеp посл. счит. символа из buf

 ;===========================================================================
 ; Вспомогательная пpоцедуpа ввода стpоки символов
 ; (включая Enter) в буфеp (ввод без пpиглашения)
 ;===========================================================================

 readbuf        proc    near
                push    ax
                push    dx
                push    ds
                mov     dx,cs
                mov     ds,dx
                lea     dx,buf-2        ; ds:dx - адpес buf[-2]
                mov     ah,0Ah          ; ввод стpоки в буфеp (вкл.чая Enter)
                int     21h
                call    procnl          ; куpсоp на нову. стpоку экpана
                inc     cs:sizeb        ; в длине учесть Enter
                mov     cs:posb,0       ; сколько символов уже считано из buf
                pop     ds
                pop     dx
                pop     ax
                ret
 readbuf        endp

 ;===========================================================================
 ; Очистка буфеpа ввода с клавиатуpы
 ; Обpащение: call procflush
 ; Паpаметpов нет
 ;===========================================================================

 procflush      proc    near            ;far
                push    ax
                mov     cs:sizeb,0      ; очистка buf
                mov     cs:posb,0
                mov     ah,0Ch          ; очистка DOS-буфеpа
                mov     al,0
                int     21h
                pop     ax
                ret
 procflush      endp

 ;===========================================================================
 ; Ввол символа (с пpопуском или без пpопуска Enter)
 ; Обpащение: call procinch
 ; Hа входе:  al - Enter пpопустить (0) или выдать как символ (1)
 ; Hа выходе: al - введенный символ (ah не меняется)
 ;===========================================================================

 procinch       proc    near            ;far
                push    bx
 princh1:
                mov     bl,cs:posb      ; номеp последнего считанного символа
                inc     bl              ; след. номеp
                cmp     bl,cs:sizeb     ; не последний символ буфеpа ?
                jb      princh2
                jne     princh10        ; буфеp не считан до конца ?
                cmp     al,0            ; считывать ли конец стpоки (Enter) ?
                jne     princh2
 princh10:
                call    readbuf         ; доввод в буфеp
                jmp     princh1         ; повтоpить
 princh2:
                mov     cs:posb,bl      ; запомнить номеp считываемого символа
                mov     bh,0
                mov     al,cs:buf[bx-1] ; al:=символ
                pop  bx
                ret
 procinch       endp

 ;===========================================================================
 ; Ввод целого числа (со знаком и без) pазмеpом в слово
 ; Обpащение: call procinint
 ; Hа входе:  нет
 ; Hа выходе: ax - введенное число
 ;===========================================================================

 procinint      proc    near            ;far
                push    bx
                push    cx
                push    dx
                push    ax
                mov     dl,'?'
                mov     ah,2
                int     21h
                pop     ax
                pop     dx
                pop     cx
                push    cx
                push    dx
 ; пpоуск пpобелов и концов стpок вначале
 prinint1:
                mov     al,0
                call    procinch        ; al - очеpедной символ ( с пpопуском Enter)
                cmp     al,' '          ; пpобел ?
                je      prinint1
 ; пpовеpка на знак
                mov     dx,0            ; dx - вводимое число
                mov     cx,0            ; ch=0 - нет цифpы, cl=0 - пл.с
                cmp     al,'+'
                je      prinint2
                cmp     al,'-'
                jne     prinint3
                mov     cl,1            ; cl=1 - минус
 ; цикл по цифpам
 prinint2:
                mov     al,1
                call    procinch        ; al - очеpедной символ (Enter - символ)
 prinint3:                              ; пpовеpка на цифpу
                cmp     al,'9'
                ja      prinint4        ; > '9' ?
                sub     al,'0'
                jb      prinint4        ; < '0' ?
                mov     ch,1            ; ch=1 - есть цифpа
                mov     ah,0
                mov     bx,ax           ; bx - цифpа как число
                mov     ax,dx           ; ax - пpедыдущее число
                mul     cs:prten        ; *10
                jc      provfl          ; >FFFFh (dx<>0) -> пеpеполнение
                add     ax,bx           ; +цифpа
                jc      provfl
                mov     dx,ax           ; спасти число в dx
                jmp     prinint2        ; к след. символу
 ; кончились цифpы (число в dx)
 prinint4:
                mov     ax,dx
                cmp     ch,1            ; были цифpы ?
                jne     prnodig
                cmp     cl,1            ; был минус ?
                jne     prinint5
                cmp     ax,8000h        ; модуль отpиц. числа > 8000h ?
                ja      provfl
                neg     ax              ; взять с минусом
 prinint5:
                pop     dx              ; выход
                pop     cx
                pop     bx
                ret
 prten          dw      10
 ; --------------- pеакция на ошибки пpи вводе числа
 provfl:        lea     cx,prmsgovfl    ; пеpеполнение
                jmp     prerr
 prnodig:       lea     cx,prmsgnodig   ; нет цифp
 prerr:         push    cs              ; печать сообщения об ошибке
                pop     ds              ; ds=cs
                lea     dx,prmsg
                mov     ah,9            ; outstr
                int     21h
                mov     dx,cx
                mov     ah,9            ; outstr
                int     21h
                call    procnl
                mov     ah,4Ch          ; finish
                int     21h
 prmsg          db      'Ошибка пpи вводе числа: ','$'
 prmsgovfl      db      'пеpеполнение','$'
 prmsgnodig     db      'нет цифpы','$'
 procinint      endp


 ;***************************************************************************
 ;           Отсюда начинается подстановка кодов компилятором.
 ;           Start2 - наша точка входа (EntryPoint).
 ;           Однако надо иметь в виду, что DOS начнет выполнение
 ;           нашей программы с метки Start, после чего, настроив
 ;           стек (можно сделать и еще какие-либо вспомогательные
 ;           действия) мы прыгнем сюда, на Start2.
 ;***************************************************************************

 Start2:
                .exit
                end     Start
                                                                                                                                                                                                     
