;       Mem736 - expander memory
;         (расширитель памяти)
; Предоставляет 736 кБайт под память DOS

cseg    segment
        assume  cs:cseg,ds:cseg,es:cseg,ss:cseg

;   Блок объявления переменных
        
nprm    equ     byte ptr ds:[0080h]  ;кол-во байтов в параметрах программы
old10   equ     dword ptr cs:[00c0h] ;старый вектор прерывания 10
old2f   equ     dword ptr cs:[00c4h] ;старый вектор прерывания 2F
prz1    equ     word ptr ds:[00c8h]  ;признак 736k(FFFF)/640k(0000)
prz2    equ     word ptr cs:[00cah]  ;признак  ВКЛ(FFFF)/ВЫКЛ(0000)
; Признак prz1 показывает какой объем оперативной памяти доступен DOS в дан-
; ный момент, а признак prz2 - активна или дезактивирована резидентная про-
; грамма в данный момент.
old08   equ     dword ptr cs:[00d0h] ;старый вектор прерывания 08
old20   equ     dword ptr cs:[00d4h] ;старый вектор прерывания 20
old21   equ     dword ptr cs:[00d8h] ;старый вектор прерывания 21

        org     100h
start:  jmp     begin

;   конец объявления переменных

;   конец заголовка


;   Процедура new2f - обработчик прерывания 2F
; Данная процедура вставляется в цепочку обработчиков прерывания 2F. Если
; регистр AH при обращении к прерыванию 2F не равен EFh, то управление пе-
; редается следующему обработчику. Если AH = EFh, то анализируется регистр
; AL. Если AL = 55h, то производится дезактивация резидентной программы,
; т.е. отключается обработчик прерывания 10h (prz2=0000h), отсоединяется
; от DOS память свыше 640к (т.е. укорачивается последний блок MCB), видео-
; адаптер инициализируется (устанавливается режим 03h через стандартный
; int 10h). Если AL = AAh, то производится активизация резидентной програм-
; мы, т.е. память видеоадаптера приводится в состояние доступности по ад-
; ресам A0000-BFFFF, память с адреса A0000 по B7FFF подключается к DOS
; (т.е. увеличивается последний блок MCB), устанавливается признак актив-
; ности программы (prz2) и признак размера памяти 736к (prz1). При AH = EFh
; в регистре AX всегда возвращается значение AA55h, для распознания наличия
; в памяти резидентной части программы.

new2f     proc      near
          cmp       ah,0efh   ;если функ.EF, то переход к обработке (на lb01),
          je        lb01      ;иначе, то переход по старому вектору.
          jmp       dword ptr cs:old2f
lb01:     push      ds        ;сохранение регистров в стеке.
          push      es
          push      dx
          cld
          mov       dx,cs     ;ds=cs
          mov       ds,dx
          xor       dx,dx     ;es=0
          mov       es,dx
;  Анализ кода подфункции:
;  55 - ОТКЛ обработчика прерывания 10
;  AA - ВКЛ  обр. прер.10
          cmp       al,55h    ;если подфунк.55, то ОТКЛ обр.прер.10,
          jne       lb02      ;если нет, то на проверку <подфунк=AA>
          call      m640      ;м640 - подпрограмма урезания MCB
                              ;до границы A0000
          mov       prz1,0000h  ;сделать признак 640к
          mov       prz2,0000h  ;сделать признак ВЫКЛ
          mov       ax,0003h  ;восстановление текстового видеорежима 03h
          int       10h       ;(80х25 символов, цвет)
out2f:    mov       bx,prz2
                              ;восстановление регистров и возврат
          pop       dx        ;из обработчика прерывания 2F
          pop       es
          pop       ds
          mov       ax,0aa55h ;признак наличия резидентной программы mem736
          iret                ;возврат из прерывания 2F
lb02:     cmp       al,0aah   ;если подфунк. AA, то ВКЛ обр.прер.10,
          jne       out2f     ;если нет, то на выход
          call      egamem    ;egamem - подпр-ма включения в рабочее
                              ;состояние памяти A0000 - B8000
          call      clrs      ;clrs - подпрограмма отчистки экрана
          call      m736      ;м736 - подпрограмма расширения MCB
                              ;до границы B8000
          mov       prz1,0ffffh ;сделать признак ON
          mov       prz2,0ffffh ;сделать признак ВКЛ
          jmp       out2f     ;на выход из прерывания
new2f     endp

;  Конец процедуры new2f

;  Процерура new10 - обработчик прерывания 10
; Это основная процедура резидентной части. Именно она осуществляет пере-
; ключение объема оперативной памяти при переходе в графические и тексто-
; вые режимы и обеспечивает нестандартную работу видеоадаптера в текстовых
; режимах при использовании памяти видеоадаптера для расширения памяти DOS.
; Данная процедура отслеживает попытки переключиться в графические режимы 
; ( функция 00h, режимы > 03h ) и отключает дополнительную память ( если
; есть необходимость ) перед установкой режима. При попытках переключиться
; в текстовый режим ( функция 00h, режимы <= 03h ) запрещается обнуление
; памяти видеоадаптера и, если необходимо, увеличивается доступная память 
; до 736к. Данная процедура подменяет обработчики функций 02h ( позициони-
; рование курсора ) и 05h ( установка активной страницы ) стандартной про-
; граммы обработки прерывания 10h. Так как видеопамять в текстовых режимах,
; при активной mem736, использууется нестандартно ( в режиме доступности по
; адресам A0000h-BFFFFh ), то для правильной работы функций 02h и 05h необ-
; ходимо вводить соответствующие поправки ( в обоих случаях поправка сос-
; тавляет число C000h ). Функция 0Eh ( вывод символа в режиме телетайпа )
; выполняется стандартным обработчиком, но позиционирование курсора после
; выполнения вывода символа производится данной процедурой. Функция 0Eh
; ( установка шрифтов ) перехвачена для восстановления доступности памяти
; видеоадаптера для DOS после ее завершения, так как стандартный обработчик
; инициализирует экран, полностью восстанавливая стандартный текстовый режим.

new10     proc      near
          push      ds
          push      es
          push      dx
          mov       dx,cs
          mov       ds,dx     ;ds=cs
          xor       dx,dx
          mov       es,dx     ;es=0
          pop       dx
          cmp       prz2,0ffffh ;если мем736 ВКЛ, то продолжить,
          jne       out10a      ;иначе на выход.
          cmp       ah,0      ;если функция 00, то обработка,
          jne       lb11      ;если нет, то переход на продолжение (на lb11).

;обработка функции 00
          cmp       al,03h    ;если режим текстовый (<=03),то далее,
          ja        lb12      ;если графический, то переход.
;обработка режимов <=03
          or        al,80h    ;установка в 1 старшего бита видеорежима,
                              ;при этом происходит установка режима без
                              ;инициализации экрана.
          pushf
          call      dword ptr cs:old10      ;переход к штатному
                                            ;обр-ку с возвратом
          and       byte ptr es:[0487h],7fh ;сброс бита 7 флагов EGA,
                                            ;это старший бит текущего
                                            ;видеорежима.
          call      egamem    ;включить в работу память A0000-B8000
          call      clrs      ;отчистить экран
          cmp       prz1,0ffffh
          je        out10     ;если память в 736к, то на выход
          call      m736      ;увеличить конечный блок MCB
          mov       prz1,0ffffh ;признак в ON
;выход из прерывания
out10:    pop       es
          pop       ds
          iret
;обработка режимов >03
lb12:     call      m640      ;уменьшить конечный блок MCB
          mov       prz1,0000h             ;признак в OFF
          pushf
          call      dword ptr cs:old10     ;переход к штатному
                                           ;обр-ку с возвратом
          jmp       out10
;конец обработки функции 00

;проверка признака 736к (prz1).
lb11:     cmp       prz1,0ffffh
          je        lb13  ;если признак 736к, то переход
                          ;к штатному обработчику без возврата
out10a:   pop       es
          pop       ds
          jmp       dword ptr cs:old10

lb13:     cmp       ah,02h    ;если функ.02 (позиц-ние курсора),то
          jne       lb14      ;обработка, иначе на продолжение

;обработка функции 02 (позиционирование курсора)
          push      ax
          push      bx
          push      cx
          push      dx
          mov       bl,bh     ;в BL номер страницы
          xor       bh,bh
          shl       bx,1      ;в BX номер страницы * 2
          add       bx,0450h  ;в ВХ адрес координат курсора
                              ;в переменных BIOS.
          mov       word ptr es:[bx],dx ;в 0040:005X коорд-ты курсора
          sub       bx,0450h
lb15:     shr       bx,1      ;в BX номер страницы
;далее процедура позиционирования курсора
          mov       ax,word ptr es:[044ah] ;в AX количество символьных столб-
                                           ;цов в текущем видеорежиме.
          mul       dh          ;в AX произведение номера строки
                                ;и количества столбцов в строке
          xor       dh,dh       ;в DX остался номер столбца
          add       ax,dx       ;позиция курсора в символах от
                                ;начала видеостраницы
          mov       cx,ax       ;cx=ax
          mov       ax,bx       ;в AX номер страницы
          mov       bx,0800h    ;0800h размер страницы в байтах
          mul       bx          ;в AX смещение стр-цы от начала видеообласти
          add       ax,cx       ;в AX смещение положения курсора от начала
                                ;видеообласти
          add       ax,0c000h   ;добавление смещения от адреса A0000h
          mov       cx,ax       ;вычисленное смещение позиции курсора в CX
          mov       dx,03d4h    ;запись в регистры 0Eh и 0Fh порта EGA 3D5h
          mov       al,0eh      ;вычисленного положения курсора.
          out       dx,al
          inc       dx
          mov       al,ch
          out       dx,al
          dec       dx
          mov       al,0fh
          out       dx,al
          inc       dx
          mov       al,cl
          out       dx,al
          pop       dx
          pop       cx
          pop       bx
          pop       ax
          jmp       out10
;конец обработки функции 02

lb14:     cmp       ah,05     ;если функция 05, то обраб-ка,
          jne       lb16      ;иначе продолжение

;обработка функции 05 (установка активной страницы)
          push      ax
          push      cx
          ;в 0040:0062 номер страницы
          mov       byte ptr es:[0462h],al  ;номер стр-цы в переменные BIOS
          xor       ah,ah     ;обнуление старш. байта AX
          mov       cx,word ptr es:[044ch]  ;размер страницы в байтах
          shr       cx,1                    ;cx=cx/2
          mul       cx
          ;теперь в AX смещение страницы от начала видеобуфера
          mov       word ptr es:[044eh],ax     ;АХ в 0040:004Е
          shr       ax,1                       ;ax=ax/2
          add       ax,0c000h                  ;в AX смещение страницы отно-
                                               ;сительно адреса A0000h.
          mov       cx,ax                      ;СХ=АХ
          call      epoint      ;установка точки входа устанавливаемой
                                ;видеостраницы.
          pop       cx
          pop       ax
          jmp       out10
;конец обработки функции 05

lb16:     cmp       ah,0eh    ;если функция 0Е, то обработка,
          jne       lb17      ;иначе продолжение

;обработка функции 0Е (вывод символа в режиме телетайпа)
          pushf
          call      dword ptr cs:old10  ;вызов штатного обр.прер.10
                                        ;с возвратом сюда
          push      ax
          push      bx
          push      cx
          push      dx
          ;в BL номер активной страницы
          ;обнуление старшего байта ВХ
          ;сдвиг ВХ влево на разряд
          ;в DX коорд-ты курсора
          mov       bl,byte ptr es:[0462h]
          xor       bh,bh
          shl       bx,1
          add       bx,0450h
          mov       dx,word ptr es:[bx]
          sub       bx,0450h
          jmp       lb15      ;на позиционирование курсора
;конец обработки функции 0Е

lb17:     cmp       ah,11h    ;если функция 11, то обработка (lb18)
          je        lb18      ;иначе на выход (out10a)
          jmp       out10a

;обработка функции 11 (установка шрифтов)
lb18:     pop       es
          pop       ds
          pushf
          call      dword ptr cs:old10  ;вызов штатного обработчика
                                        ;прер.10 с возвратом
          call      egamem    ;активирование памяти А0000-В8000
          iret
;конец обработки функции 11

new10     endp

;  Конец процедуры new10


;  Процедура new08 - обработчик прерывания 08
; Данная процедура добавлена из-за того что многие программы сами пере-
; ключают видеоадаптеры дисплеев в графические режимы не пользуясь при
; этом прерыванием 10h. Естественно что программа mem736 не может отклю-
; чить дополнительную память от DOS при таком переключении. Поэтому по-
; явилась необходимость постоянно отслеживать доступность памяти свыше
; 640к и отключать дополнительную память при обнаружении ее недоступ-
; ности. Данная процедура перехватывает прерывание 08h ( тики таймера ),
; которое вызывается таймером аппаратно примерно 18.2 раза в секунду.
; Перед передачей управления стандартному обработчику прерывания 08h,
; данная процедура производится проверку доступности памяти по адресу
; A0000h, и при обнаружении ее недоступности отключает дополнительную
; память от DOS.

new08   proc    near
        push    ax
        push    es
        xor     ax,ax
        mov     es,ax                     ;es=0
        cmp     word ptr es:[0413h],02e0h ;проверка на 736к, если это так,
        pop     es                        ;то переход на проверку доступ-
        pop     ax                        ;ности памяти за границей A0000h,
        jz      lb21                      ;если нет, то к стандартному об-
        jmp     dword ptr cs:old08        ;работчику прерывания 08h.
lb21:   push    ds
        push    ax
        mov     ax,0a000h
        mov     ds,ax
        mov     ax,word ptr ds:[1000h]     ;это проверка памяти за границей
        not     word ptr ds:[1000h]        ;А0000h на способность работать.
        not     ax
        cmp     ax,word ptr ds:[1000h]     ;если память не работает,
        jne     lb22                       ;то переход,
        not     word ptr ds:[1000h]        ;иначе востанавливаем ячейку
        pop     ax                      ;А000:1000h, и передаем
        pop     ds                      ;управление старому обработчику
                                        ;прерывания 08.
        jmp     dword ptr cs:old08
lb22:   pop     ax
        pop     ds                      ;переход к штатному обраб.прер.08
        pushf
        call    dword ptr cs:old08      ;с возвратом сюда.
        push    es
        push    ds
        push    ax
        mov     ax,cs           ;ds=cs
        mov     ds,ax
        xor     ax,ax           ;es=0
        mov     es,ax
        call    m640            ;урезание памяти до 640к
        mov     prz1,0000h      ;признак 640к
        pop     ax              ;востановление регистров и
        pop     ds              ;возврат из прерывания
        pop     es
        iret
new08   endp

;  Конец процедуры new08


;  Процедура new12 - обработчик прерывания 12
; Данная процедура заменяет собой стандартный обработчик прерывания 12h
; находящийся в ROM BIOS. Она возвращает в регистре AX слово по адресу 
; 0000:0413h ( количество доступной памяти в килобайтах ). Эта процедура
; добавлена из-за того, что некоторые BIOS принимают число большее
; 0280h ( 640k ) как ошибку и округляют его до 0280h.

new12   proc    near
        push    es      ;Этот обработчик загружает в AX слово по
        xor     ax,ax   ;адресу 0000:0413h ( кол-во доступной памяти
        mov     es,ax   ;в килобайтах ).
        mov     ax,word ptr es:[0413h]
        pop     es
        iret
new12   endp

;  Конец процедуры new12


;  Процедура new20 - обработчик прерывания 20
; Данная процедура отслеживает прерывание 20h ( завершение программы ),
; и если при этом vtv736 активна и память выделенная DOS составляет 
; 640к ( что бывает если программа входит в список несовместимых про-
; грамм или если она использует графику ), то производится восстановле-
; ние объема памяти 736к путем установки текстового режима 03h через 
; вызов прерывания 10h. ( При этом работает процедура new10. )

new20   proc    near            ;если mem736 дезактивирована, то перейти
        cmp     prz2,0000h      ;к стандартному обработчику.
        jz      out20
        push    ax              ;сохранение используемых регистров
        push    es
        xor     ax,ax
        mov     es,ax           ;es=0
        cmp     word ptr es:[0413h],02e0h       ;если память 736к, то
                                                ;к стан-му обработчику,
        pop     es                              ;если нет, то
        jz      lb90            ;восстанавливается
        mov     ax,0003h        ;видеорежим 03, и вызывается
        int     10h             ;штатный обработчик.
lb90:   pop     ax
out20:  jmp     dword ptr cs:old20
new20   endp

;  Конец процедуры new20


;  Процедура new21 - обработчик прерывания 21
; Данная процедура отслеживает функции 00h, 4Ch и 4Bh прерывания 21h ( вы-
; зов функций DOS ). Реакция на функции 00h и 4Ch (завершить выполнение
; программы ) аналогична реакции процедуры new20. То есть при наличии па-
; мяти в 640к и активности mem736 память расширяется до 640к и устанавли-
; вается текстовый режим 03h видеоадаптера. Функция 4Bh перехвачена для
; отключения дополнительной памяти при запуске программ несовместимых с
; мем736. Функция 4Bh используется DOS при запуске программ на выполнение,
; при этом в паре регистров DS:DX передается указатель на строку с именем
; программы. Данная процедура проверяет это имя на совпадение с одним из
; имен из массива nprg, и при обнаружении совпадения отключает дополнитель-
; ную память полностью восстанавливая стандартное состояние видеоадаптера
; или ограничиваясь только урезанием последнего блока MCB. Массив имен
; nprg и переменная nname содержащая количество имен в массиве формируют-
; ся нерезидентной частью программы мем736 при первом запуске программы
; на основании списка имен из файла mem736.lpr. Массив имен имеет следу-
; ющую структуру: каждое имя занимает тринадцать байт и кончается байтами
; 00h или 01h. Байт 00h в конце имени означает что надо урезать память до
; 640к без полного восстановления стандартного режима видеоадаптера, а
; байт 01h означает что надо восстановить стандартный режим видеоадаптера
; полностью.

new21   proc    near
        cmp     prz2,0000h      ;если mem736 дезактивированна, то перейти
        jnz     nnext           ;к стандартному обработчику (out21).
        jmp     out21
nnext:  cmp     ah,4bh  ;если функция 4B (EXEC), то на обработку по EXE00,
        jz      exe00   ;иначе на продолжение по lb40
        jmp     lb40
;  Обработчик функции 4b. Осуществляет контроль за именами запускаемых
;  программ. Если имя есть в списке nprg, то доп. память отключается.
exe00:  pushf
        push    ds      ;сохранение всех используемых регистров.
        push    es
        push    ax              ;Функция EXEC передает указатель на
        push    bx              ;строку с именем запускаемой программы
        push    cx              ;в регистрах DS:DX
        push    dx
        push    si
        push    di
        push    bp
        cld
        mov     si,dx
        dec     dx
exe01:  lodsb
        cmp     al,00h  ;поиск нулевого байта в строке с именем
        jne     exe01   ;запускаемой программы.
        dec     si      ;SI указывает на нулевой байт
        std
exe02:  lodsb
        cmp     al,5ch  ;поиск начала имени программы. Имя начинается
        je      exe03   ;после символов "\" (5С), ":" (3А), или
        cmp     al,3ah  ;с самого начала строки.
        je      exe03
        cmp     si,dx
        jne     exe02
        jmp     short exe04
exe03:  inc     si
exe04:  inc     si      ;SI указывает на начало имени программы.
        mov     dx,si   ;смещение имени прогр. в DX.
        mov     bp,offset nprg  ;смещение массива имен в BP
        mov     cx,cs:nname     ;счетчик имен в массиве nprg
        cmp     cx,0            ;если в nprg имен 0, то выход.
        jz      outexe
        mov     ax,cs
        mov     es,ax           ;ES:=CS
        xor     di,di           ;DI:=00
exe05:  mov     al,byte ptr ds:[si]     ;взять символ из имени прог.
        mov     bl,byte ptr es:[di+bp]  ;взять символ из массива имен.
        cmp     bl,02h          ;Если симв. из массива < 02,то это
        jae     exe06           ;означает совпадение имен, и производится
                                ;проверка символа из имени запускаемой
                                ;программы на соответствие
        cmp     al,2eh          ;символам "." или "nul".
        je      exeoff          ;Если соответствует то выкл. доп. памяти.,
        cmp     al,00h          ;иначе на EXE08.
        je      exeoff
        jmp     exe08
exeoff: cmp     bl,00h          ;анализ последнего байта в имени программы
        jne     exesp           ;в массиве nprg, если 0, то урезаем память
        mov     ax,cs           ;до 640к, без возврата к старому распреде-
        mov     ds,ax           ;лению видеопамяти. (т.е. видеопамять ос-
        xor     ax,ax           ;тается доступной по адресам A0000h).
        mov     es,ax           ;если 01h, то производится полное восста- 
        call    m640            ;новление режима 03h видеоадаптера.
        mov     prz1,0000h
        jmp     outexe
exesp:  mov     ax,0ef55h
        int     2fh
        mov     prz2,0ffffh
outexe: pop     bp
        pop     di      ;Востановление регистров и переход к
        pop     si      ;штатному обработчику.
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        pop     es
        pop     ds
        popf
        jmp     dword ptr cs:old21
exe06:  cmp     al,41h          ;Если символ в верхнем регистре
        jb      exe07           ;( >41h и <5Bh ), то переводим его
        cmp     al,5bh          ;в нижний регистр.
        jae     exe07
        or      al,20h
exe07:  cmp     al,bl           ;Если символы не равны, то переходим,
        jne     exe08           ;иначе к следующим символам.
        inc     si
        inc     di
        jmp     exe05
exe08:  add     bp,000dh        ;Указатель на следующий элемент
        mov     di,0000h        ;массива имен.
        mov     si,dx           ;Указатель на начало имени прогр.
        loop    exe05
        jmp     outexe
;  Конец обработчика функции EXEC (4B).
lb40:   push    ax      ;сохранение регистров AX и ES в стеке
        push    es      
        xor     ax,ax
        mov     es,ax                           ;es=0
        cmp     word ptr es:[0413h],02e0h       ;если память 736к, то вос-
        pop     es                              ;тановить регистры и перей-
        pop     ax                      ;ти к стандартному обработчику пре-
        jz      out21           ;рывания 21h, если нет, то
        cmp     ah,00h          ;восстанавить стандартный
        jz      lb41            ;видеорежим 03 при кодах функций
        cmp     ah,4ch          ;00 и 4С (завершить выполнение
        jnz     out21           ;программы), и вызывать штатный
lb41:   push    ax              ;обработчик.
        mov     ax,0003h
        int     10h
        pop     ax
out21:  jmp     dword ptr cs:old21
new21   endp

;  Конец процедуры new21


;  Подпрограмма egamem - включает в работу память EGA или VGA адаптера
; Данная процедура предназначенна для обеспечения доступности памяти видео-
; адаптера для процессора по адреса A0000h-BFFFFh. Для этого в регистр 06h
; ( управляющие функции графики ) порта видеоадаптера 3CFh выводится байт
; 02h, что означает: бит0=0 - текстовый режим, бит1=1 - сцепление нечетных
; плоскостей видеопамяти после четных, бит2/3=00 - отображение памяти для
; процессора начиная с адреса A0000h. Предварительно в порт 3CEh выводится
; номер регистра порта 3CF - 06h.

egamem    proc      near
          push      ax          ;сохранение регистров
          push      dx
          mov       dx,03ceh
          mov       al,06h
          out       dx,al       ;в порт 3CEh байт 06h
          inc       dx
          mov       al,02h
          out       dx,al       ;в порт 3CFh байт 02h
          pop       dx          ;восстановление регистров
          pop       ax
          push      cx
          mov       cx,0c000h   ;в CX новая точка входа для видеобуфера
          call      epoint      ;вызов процедуры установки точки входа
          pop       cx
          ret                   ;возврат из процедуры
egamem    endp

;  Конец подпрограммы egamem


;  Подпрограмма epoint - установка точки входа видеобуфера
; Данная процедура устанавливает точку входа текущей видеостраницы от
; начала видеобуфера. Точка входа при вызове процедуры передается в
; регистре CX. Так как видеобуфер после выполнения процедуры egamem
; начинается с адреса A0000h, то для нулевой страницы требуется смеще-
; ние (B8000h-A0000h)/2=C000h. При выполнении процедуры egamem именно
; это значение передается в качестве точки входа. При выполнении про-
; цедуры new10, функции 05h ( установка активной видеостраницы ), это
; смещение добавляется к вычисленному в процедуре. Слово из СХ записы-
; вается в пару регистров 0Ch (старший) и 0Dh (младший) порта 3D5h, при
; адресации этих регистров используется порт 3D4h.

epoint    proc      near
          push      ax          ;сохранение регистров в стеке
          push      dx
          mov       al,0ch
          mov       dx,03d4h
          out       dx,al       ;вывод в порт 3D4h номера регистра 0Ch
                                ;порта 3D5h
          mov       al,ch
          inc       dx
          out       dx,al       ;вывод в порт 3D5h содержимого регистра CH
          mov       al,0dh
          dec       dx
          out       dx,al       ;вывод в порт 3D4h номера регистра 0Ch
                                ;порта 3D5h
          mov       al,cl
          inc       dx
          out       dx,al       ;вывод в порт 3D5h содержимого регистра CL
          pop       dx          ;восстановление регистров и
          pop       ax          ;возврат из процедуры
          ret
epoint    endp

;  Конец подпрограммы epoint


;  Подпрограмма clrs - отчистка экрана
; Данная процедура очищает экран при помощи стандартной функции 06h пре-
; рывания 10h из BIOS видеоадаптера.

clrs      proc      near
          push      ax
          push      bx
          push      cx
          push      dx
          mov       ax,0600h    ;код функции отчистки экрана в AX
          mov       bh,07h      ;атрибуты заполнения
          xor       cx,cx       ;в CX коорд-ты верхнего левого угла
          mov       dx,184fh    ;в DX коорд-ты нижнего правого угла
          int       10h         ;собственно отчистка
          pop       dx
          pop       cx
          pop       bx
          pop       ax
          ret
clrs      endp

;  Конец подпрограммы отчиски экрана


;         м736 - подпрограмма расширения последнего блока МСВ
;                      до границы памяти B7FFF.
; Данная процедура проверяет захватывает ли последний блок МСВ адреса
; до B7FFFh, если да, то ничего не предпринимается, а если нет, то этот
; блок расширяется на 18000h байт, а так же по адресу 0000:0413 записы-
; вается число 02Е0h ( 736кбайт доступной памяти ). Затем производится
; проверка на наличие в последнем блоке МСВ префикса программного сег-
; мента PSP и если он обнаруживается, то в нем увеличивается поле пос-
; леднего используемого параграфа памяти на 1800h.

m736      proc      near
          push      ax
          push      dx
          call      zmcb      ;к подпрограмме поиска последнего МСВ
          mov       ax,es
          add       ax,word ptr es:[0003h]
          inc       ax
          cmp       ax,0b800h ;если MCB уже расширен до B800,
          jz        lb71      ;то заканчиваем.
          add       word ptr es:[0003h],1800h  ;расшир. МСВ на 96к
          push      es
          xor       dx,dx
          mov       es,dx                      ;увеличение значения объема
          mov       word ptr es:[0413h],02e0h  ;доступной памяти в перемен-
          pop       es                         ;ных BIOS ( 0000:0413h ).
          cmp       word ptr es:[0010h],20cdh
;если в последнем МСВ находится PSP (первая команда int 10h),
;то увеличиваем последний используемый параграф в PSP на 1800h.
          jne       lb71
          add       word ptr es:[0012h],1800h
lb71:     xor       dx,dx
          mov       es,dx     ;ES=0
          pop       dx
          pop       ax
          ret
m736      endp

;  Конец подпрограммы m736


;  ZMCB - подпрограмма поиска последнего блока МСВ
; Данная процедура начинает поиск последнего блока МСВ с блока принад-
; лежащего самой программе, последний блок идентифицируется по байту
; 'Z' в начале блока. Сегмент последнего блока процедура возвращает в
; регистре ES.

zmcb      proc      near
          mov       dx,cs
          dec       dx
          mov       es,dx       ;теперь ES указывает на блок МСВ
                                ;принадлежащий данной программе
lbzm1:    cmp       byte ptr es:[0000h],5ah     ;если первый байт блока
          jne       lbzm2                       ;МСВ равен 'Z', то это 
          ret                                   ;последний блок и проце-
                                                ;дура завершается.
lbzm2:    mov       ax,word ptr es:[0003h]      ;в АХ длина блока в пара-
                                                ;графах.
          mov       dx,es                       ;DX=ES
          add       ax,dx
          inc       ax                          ;AX=AX+DX+1
          mov       es,ax       ;теперь ES указывает на следующий
                                ;блок МСВ
          jmp       lbzm1       ;переход на проверку блока на последний
zmcb      endp

;  Конец подпрограммы ZMCB


;         m640 - подпрограмма урезания последнего блока МСВ
;                   до границы памяти A0000
; Данная процедура вначале проверяет не превышает ли память выделенная
; последнему блоку МСВ границу A0000h, и если это так, то никаких дей-
; ствий не предпринимается. Если последний блок МСВ превышает границу
; A0000h, то он урезается на 18000h байт, а так же по адресу 0000:0413h
; записывается слово 0280h ( 640kбайт доступной памяти ). Затем произ-
; водится проверка на наличие в последнем блоке МСВ префикса програм-
; много сегмента PSP, и если он там, то уменьшается в нем поле послед-
; него используемого параграфа памяти на 1800h.

m640      proc      near
          push      ax
          push      dx
          call      zmcb      ;поиск последнего блока МСВ
          add       ax,word ptr es:[0003h]
          cmp       ax,9fffh  ;если MCB уже ниже A0000,
          jbe       jlb71     ;то выходим.
          sub       word ptr es:[0003h],1800h     ;урезание блока МСВ
          push      es
          push      cx
          push      di
          mov       dx,0a000h                     ;обнуление видеопамяти
          mov       es,dx                         ;занятой под оперативку
          xor       di,di
          mov       cx,8000h
          xor       ax,ax
          cld
rep       stosw
          mov       dx,0b000h
          mov       es,dx
          xor       di,di
          mov       cx,4000h
rep       stosw
          xor       dx,dx                         ;уменьшение значения
          mov       es,dx                         ;объема доступной па-
          mov       word ptr es:[0413h],0280h     ;мяти в переменных
          pop       di                            ;BIOS ( 0000:0413h ).
          pop       cx
          pop       es
          cmp       word ptr es:[0010h],20cdh     ;если в МСВ задача,
          jne       jlb71                         ;то урезаем выделенную
          sub       word ptr es:[0012h],1800h     ;ей память
jlb71:    jmp       lb71
m640      endp

;  Конец подпрограммы м640


;количество имен в массиве nprg
nname   dw      (?)

;  Массив имен программ для которых необходимо отключатьдоп. память.
;  Каждое имя начинается на границе 13 байт, содержит максимум
;  12 символов, и заканчивается нулевым байтом.

nprg    db      3328 dup(0)

;  Конец массива имен программ.


;  Начало не резидентной части программы

begin:
;  Анализ параметров введенных из командной строки
        mov     cl,nprm ;nprm - кол-во байтов в параметрах
        cmp     cl,01h  ;если параметров нет (nprm<=1),
        ja      lbl03   ;то nprm=0.
        mov     nprm,00h
        jmp     lbl09
exit1:  call    printscreen
        int     20h
;  Далее анализ введенного параметра
;  /help - nprm=01, /off - nprm=02, /on - nprm=03.
lbl03:  xor     ch,ch
        mov     si,0081h        ;поиск символа '/'
lbl04:  lodsb
        cmp     al,2fh
        loopne  lbl04
        cmp     cx,0000h         ;если символ '/' не найден,
        jne     lbl06            ;то выводим сообщение errprm об
lbl05:  mov     dx,offset errprm ;ошибке в параметрах.
        jmp     exit1
lbl06:  lodsb
        cmp     al,3fh          ;символ = '?',
        je      lbl06a
        or      al,20h          ;переводим символ в нижний регистр
        cmp     al,68h          ;символ = 'h',
        jne     lbl07           ;если нет, то на lbl07,
lbl06a: mov     dx,offset help  ;если да, то выводим справку и выходим
        jmp     exit1
lbl07:  cmp     al,6fh  ;если символ не 'o', то выводим сообщение
        jne     lbl05   ;об ошибке в параметрах.
        lodsb           ;грузим следующий символ
        or      al,20h  ;переводим его в нижний регист
        cmp     al,66h  ;если символ 'f', то nprm=02,
        jne     lbl08   ;иначе продолжаем по lbl08
        mov     nprm,02h
        jmp     lbl09
lbl08:  cmp     al,6eh  ;если символ не 'n', то выводим сообщение
        jne     lbl05   ;об ошибке в параметрах.
        mov     nprm,03h        ;nprm=03, что значит параметр=/on
;  Конец анализа параметров
lbl09:  cmp     word ptr ds:[0002h],0a00h
        jae     lbl01           ;если память в системе ниже 640К,
        mov     dx,offset izv2  ;то вывод извинений и выход.
exit2:  mov     ax,0900h
        int     21h
        int     20h
lbl01:  xor     ax,ax
        push    es
        mov     es,ax     ;ES указывает на начало памяти
        mov     al,byte ptr es:[0487h]   ;проверка наличия EGA-256k
        pop     es
        and     ax,0060h  ;если EGA_256k, то продолжить по lbl01a
        cmp     ax,0060h  ;иначе выход с извинениями
        jz      lbl02
        mov     dx,offset izv1      ;загрузка смещения извинений
        jmp     short exit2
lbl02:  mov     ax,0ef00h       ;проверка на наличие резидентной части
        int     2fh             ;программы в системе.
        cmp     ax,0aa55h       ;если есть резидент, то на lbl12,
        jne     lbl10           ;иначе продолжить
        jmp     lbl12
lbl10:  cmp     nprm,02h         ;если параметр /OFF и нет резидента,
        jne     lbl11            ;то грязно ругаемся и выходим,
        mov     dx,offset rugat1 ;иначе переход на cp1
        jmp     exit1

hand    dw      0       ;хендл файла Mem736.com
longm   dw      0       ;длина файла Mem736.com

;  Установка программы резидентно и активирование ее,
;то есть подключение дополнительных 96 Кбайт памяти.
lbl11:  mov     ax,3510h        ;получение в ES:BX вектора прер.10
        int     21h
        mov     word ptr ds:[00c0h],bx  ;сохранение вектора прер.10 в
        mov     bx,es                   ;ячейке old10 (DS:00C0)
        mov     word ptr ds:[00c2h],bx
        mov     ax,352fh        ;получение и сохранение в (DS:00C4)
        int     21h             ;вектора прерывания 2F
        mov     word ptr ds:[00c4h],bx
        mov     bx,es
        mov     word ptr ds:[00c6h],bx
        mov     ax,3508h        ;получение и сохранение в (DS:00D0)
        int     21h             ;вектора прерывания 08
        mov     word ptr ds:[00d0h],bx
        mov     bx,es
        mov     word ptr ds:[00d2h],bx
        mov     ax,3520h        ;получение и сохранение в (DS:00D4)
        int     21h             ;вектора прерывания 20
        mov     word ptr ds:[00d4h],bx
        mov     bx,es
        mov     word ptr ds:[00d6h],bx
        mov     ax,3521h        ;получение и сохранение в (DS:00D8)
        int     21h             ;вектора прерывания 21
        mov     word ptr ds:[00d8h],bx
        mov     bx,es
        mov     word ptr ds:[00dah],bx
        mov     dx,offset new2f ;установка нового вектора прер.2F
        mov     ax,252fh
        int     21h
        mov     prz1,0000h      ;обнуление признаков активной работы
        mov     prz2,0000h      ;программы Mem736.
        mov     dx,offset new10 ;установка нового вектора прер.10
        mov     ax,2510h
        int     21h
        mov     dx,offset new08 ;установка нового вектора прер.08
        mov     ax,2508h
        int     21h
        mov     dx,offset new12 ;установка нового вектора прер.12
        mov     ax,2512h
        int     21h
        mov     dx,offset new20 ;установка нового вектора прер.20
        mov     ax,2520h
        int     21h
        mov     dx,offset new21 ;установка нового вектора прер.21
        mov     ax,2521h
        int     21h
        mov     ax,0efaah       ;активизация программы
        int     2fh
        mov     dx,offset salut ;вывод приветствия
        call    printscreen
        mov     ax,ds           ;востановление регистра ES
        mov     es,ax


;  Программа внесения в массив NPRG имен программ из файла Mem736.lpr.
;  Каждое имя начинается на границе 13 байт, имена вносятся в нижнем
;  регистре. Всего возможно 256 имен. Файл mem736.lpr обычный текс-
;  товый, имена программ занимают по одной строке, пробелы, символы
;  табуляции и тому подобные символы в начале строки игнорируются.
;  Если перед именем программы стоит символ '+', то это означает, что 
;  перед урезанием памяти перед запуском этой программы необходимо
;  полностью восстановить стандартный режим видеоадаптера. Этим зани-
;  мается процедура new21h, на основании последнего ограничивающего
;  байта в имени программы в массиве nprg. Если этот байт равен 00h,
;  то память просто урезается до 640к, распределение памяти видеоадап-
;  тера не изменяется. Если этот байт равен 01h, то стандартный режим
;  видеоадаптера восстанавливается полностью. Файл mem736.lpr ищется в
;  том же каталоге где расположена программа mem736.com, каталог берет-
;  ся из нестандартного блока envirovment, блок МСВ которого непосред-
;  ственно предшествует блоку МСВ программы mem736.com.

        cld
        mov     cx,1000h
        mov     di,8000h
        xor     ax,ax
rep     stosw           ;обнуление буфера по смещению 8000 (всего 8 Кбайт).
        mov     ax,word ptr ds:[002ch]
        push    ds
        mov     ds,ax   ;сегмент с переменными среды в DS
        xor     si,si
lprg1:  lodsb
        cmp     al,00h
        jne     lprg1
        lodsb
        cmp     al,00h  ;поиск двух нулевых байтов.
        jne     lprg1
        lodsw
        mov     di,offset listprg
        mov     cx,0080h
rep     movsb           ;Перенос имени программы в переменную LISTPRG
        pop     ds
        mov     si,offset listprg
lprg2:  lodsb
        cmp     al,2eh  ;Поиск точки в имени файла.
        jne     lprg2
        mov     byte ptr ds:[si],4ch    ;Формирование расширения LPR и
        mov     byte ptr ds:[si+1],50h  ;запись нулевого байта в конец.
        mov     byte ptr ds:[si+2],52h
        mov     byte ptr ds:[si+3],00h
        mov     ax,3d00h
        mov     dx,offset listprg
        int     21h                     ;Попытка открыть файл Mem736.lpr.
        jnc     lprg4           ;Если неудачно, то
lprg3:  mov     dx,offset nrlpr ;Вывод сообщения о неудаче в формировании
                                ;массива с именами программ не работающих
        call    printscreen     ;под управлением Mem736.
        mov     cx,0100h
        jmp     lblrez
lprg4:  mov     hand,ax         ;Сохранение хендла файла.
        mov     bx,ax           ;Хендл в BX.
        mov     ax,3f00h        ;Функция чтения файла.
        mov     dx,8000h        ;Смещение буфера в DX.
        mov     cx,1800h        ;Предельная длина файла (6144).
        int     21h
        jnc     lprg5
        mov     ax,3e00h        ;Функция закрытия файла.
        mov     bx,hand         ;Хендл в BX.
        int     21h
        jmp     lprg3
lprg5:  mov     ax,3e00h        ;Функция закрытия файла.
        mov     bx,hand         ;Хендл в BX.
        int     21h
        mov     bp,offset nprg  ;смещение массива имен в BP
        mov     si,8000h        ;смещение буфера в SI
        xor     di,di           ;смещение первого символа в массиве имен в DI
        mov     cx,0100h        ;счетчик = 256 имени.
lprg55: mov     byte ptr ds:[00f8h],00h ;обнуление байта DS:[00F8h]
lprg56: lodsb
        cmp     al,00h  ;если очередной символ 00h, то это конец списка
        je      lblrez  ;программ, и мы переходим на завершение.
        cmp     al,21h  ;все символы меньшие или равные пробелу 
        jb      lprg56  ;игнорируются.
        cmp     al,2bh  ;если символ '+', то записываем в DS:[00F8h] байт 01h
        jne     lprg57
        mov     byte ptr ds:[00f8h],01h
        jmp     short lprg56
lprg57: dec     si
lprg6:  lodsb                   ;Ввести символ из буфера.
        cmp     al,00h          ;Если символ = 0, то это конец файла,
        jne     lprg7           ;и следовательно переход к концу программы,
        jmp     lblrez          ;иначе продолжить по LPRG7.
lprg7:  cmp     al,21h          ;если этот символ (>='пробел'), то переход
        jae     lprg9
        cmp     al,0dh          ;Если символ не перевод строки, то
        jne     lprg6           ;переходим к дальнейшим проверкам по LPRG9,
lprg75: dec     cx              ;иначе уменьшаем счетчик имен,
        cmp     cx,0000h        ;и если он равен нулю, то кончаем
        jne     lprg8           ;с этим,иначе по LPRG8 к следующему
        jmp     lblrez          ;элементу массива имен.
lprg8:  mov     dl,byte ptr ds:[00f8h]
        mov     byte ptr ds:[bp+di],dl  ;в конец имени байт DS:[00F8h]
        add     bp,000dh        ;смещение следующего элемента в BP.
        xor     di,di           ;смещение первого символа в элементе в DI.
        inc     si              ;SI указывает на следующее имя в буфере.
        jmp     lprg55          ;На дальнейшую обработку.
lprg9:  cmp     al,41h          ;Проверка на верхний регистр,
        jb      lprga           ;и если это так, то перевод в
        cmp     al,5bh          ;нижний регистр.
        jae     lprga
        or      al,20h
lprga:  mov     byte ptr ds:[bp+di],al  ;сохранение символа в массиве.
        inc     di              ;указываем на следующий символ в массиве.
        cmp     di,000ch        ;Если это 12й символ в имени,
        jne     lprg6           ;то к следующему элементу массиве,
lprgb:  lodsb                   ;иначе по LPRG6 продолжить формирование
        cmp     al,0dh          ;массива.
        jne     lprgb
        jmp     lprg8

;  Конец программы внесения имен в массив NPRG.


;  В DX адрес последнего используемого байта в резидентной части + 1
lblrez: mov     ax,0101h        ;максимальное количество имен в nprg 0101h
        sub     ax,cx           ;в АХ количество имен в nprg
        mov     nname,ax        ;сохранение количества имен в nname
        mov     cx,000dh
        mul     cl
        add     ax,4            ;теперь в АХ длина массива nprg с
                                ;небольшим запасом
        mov     dx,offset nprg
        add     dx,ax           ;в DX длина резидентной части
        int     27h             ;завершить и остаться резидентно
;  Конец установки программы резидентно

;  А сюда мы попадаем если программа уже сидит в памяти
lbl12:  cmp     nprm,00h        ;если нет параметров, то продолжать,
        jne     lbl14           ;иначе переход на lbl14
        mov     ax,0ef00h
        int     2fh             ;если Mem736 активна,
        cmp     bx,0ffffh       ;то подолжать,
        jne     lbl13           ;иначе переход на lbl13
        mov     dx,offset mon   ;выдаем сообщение Mem736 is ON
        jmp     exit1
lbl13:  mov     dx,offset moff  ;выдаем сообщение Mem736 is OFF
        jmp     exit1
lbl14:  cmp     nprm,02h  ;если параметр /OFF,
        jne     lbl15     ;то выключаем Mem736,
        mov     ax,0ef55h ;иначе включаем (по lbl15).
        int     2fh
        mov     word ptr ds:[0002h],0a000h
        mov     dx,offset moff
        jmp     exit1
lbl15:  mov     ax,0efaah
        int     2fh
        mov     dx,offset mon
        jmp     exit1

;  Конец программы

listprg db      128 dup(?)      ;Строка с именем файла Mem736.lpr.

include prnscr.asm

;  Сообщения

errprm  db      13,10,7,255,94h
        db      '         Ошибка в параметрах программы.          ',13,10,255,7
        db      'Для получения справки воспользуйтесь опцией /Help.'
        db      13,10,0
help    db      13,10,13,10,255,0ch
        db      '      Мemory 736 Kbytes ( MEM736.COM ) Программа расширения оперативной',13,10
        db      '      памяти под DOS  за счет видеобуфера адаптера EGA или VGA дисплея.',13,10,255,3
        db      ' version 1.0, Copyright (C) Alexander Andreev, Russia, Magadan, October 1993.',13,10
        db      ' ГПСИ "Россвязьинформ", г.Магадан, тел. 2-48-11, RELCOM: root@gpsi.magadan.su',13,10,13,10,255,7
        db      '      Объем  ОП  предоставляемой под  DOS увеличивается  с 640 до 736 Кбайт.',13,10
        db      ' Программа работает при наличии  минимум  640  Кбайт  оперативной  памяти  и',13,10
        db      ' адаптера  EGA или VGA с объемом видеопамяти не менее  256 Кбайт.  Так как в',13,10
        db      ' графических режимах адаптеры EGA и VGA используют свою память полностью, то',13,10
        db      ' задачи использующие графику будут располагать обычным объемом памяти (640К).',13,10
        db      ' Все задачи,работающие только в текстовых режимах,получают в свое распоряже-',13,10
        db      ' ние дополнительно 96 Кбайт памяти.  Некоторые программы  не могут нормально',13,10
        db      ' работать при активной Mem736 и для того,  чтобы такая программа запускалась',13,10
        db      ' без проблем, надо,или дезактивировать Mem736, или,что гораздо лучше, внести',13,10
        db      ' имя этой программы в файл Mem736.lpr, который должен находиться в одном ка-',13,10
        db      ' талоге  с  программой Mem736.com.  Подробности в файле Mem736.doc.',13,10,13,10,255,12
        db      ' P.S.',255,5,'     Автор не имеет возражений против свободного распространения данной',13,10
        db      '          программы и модификации ее кода.',255,7,13,10,13,10
        db      '     Опции программы:   /HELP,/H,/? - получение данной справки',13,10
        db      '                        /OFF        - дезактивирование резидентной программы',13,10
        db      '                        /ON         - активирование резидентной программы',13,0
mon     db      13,10,255,4,' Mem736 в активном состоянии.',13,10,0
moff    db      13,10,255,4,' Mem736 дезактивирована.',13,10,0
salut   db      27,25,7,255,15
        db      '        Mem736  -  программа  расширения памяти  для DOS  до ',255,8fh,'736',255,15,' Кбайт.',13,10,255,11
        db      '                (version 1.0), Copyright (C) Alexander Andreev.',13,10
        db      '                     Russia,  Magadan,  October  1993.',13,10,13,10,255,7
        db      '        Опции:   /HELP,/H,/? - получение справки',13,10
        db      '                 /OFF        - дезактивирование резидентной программы',13,10
        db      '                 /ON         - активирование резидентной программы',13,25,23,0
rugat1  db      13,10,7,255,12
        db      '        Как по вашему,  что может означать попытка'
        db      ' дезактивации программы',13,10
        db      ' которой нет еще в памяти? Если вы вчера провели'
        db      ' бурную ночь, то я за это',13,10
        db      ' ответственности не несу.',13,10,0
nrlpr   db      7,27,13,10,13,10,255,9ch
        db      '                Не могу прочитать файл Mem736.'
        db      'lpr.                       ',13,10,13,10,255,3
        db      ' В этом файле должны находиться имена программ'
        db      ' не работающих при активном',13,10
        db      ' состоянии Mem736. Этот файл должен находиться'
        db      ' в той же директории что и',13,10
        db      ' программа Mem736.com.',13,10,13,10,7,255,12
        db      '      ВНИМАНИЕ :   Имена запускаемых программ не будут'
        db      ' отслеживаться,',13,10
        db      '                   возможны зависания и некорректная'
        db      ' работа отдельных',13,10
        db      '                   программ.',13,10,0
izv1    db      13,10,7
        db      ' Прошу прощения, не могу включить память'
        db      ' EGA адаптера.',13,10
        db      ' Или у вас его нет или он не имеет память'
        db      ' необходимого размера. ( 256k )',13,10
        db      ' Очень сожалею, но работать в таких'
        db      ' условиях выше моих сил.',13,10,36
izv2    db      13,10,7
        db      ' Прошу прощения, в Вашем компьютере память имеет объем'
        db      ' меньше 640 Кбайт.',13,10
        db      ' Очень сожалею, но работать в таких'
        db      ' условиях выше моих сил.',13,10,36

;  Конец сообщений

cseg   ends             ;  Конец текста программы !!!
       end      start
