Дата: 10:07:23; 21.02.2003.


Я, Ефремов А. В., собираюсь написать драйвер XMS-памяти для ОС MS-DOS®. Я всё просчитал: драйвер будет работать с памятью, начинающейся с физического адреса 10FFF016. Это как раз байт, следующий за относительным адресом [FFFF16:FFFF16]. С последней памятью могут работать все 16-разрядные программы в реальном режиме работы ЦП (8086™ Real Address Mode), в крайнем случае, с поддержкой со стороны драйвера "HiMem.sys", который включает адресную линию A20. А вот, начиная с адреса 10FFF016, микропроцессор придётся перевести в защищённый режим работы ЦП (Protected Virtual Address Mode), потому что только тогда включается 32-битная адресация, и становится, следовательно, доступной расширенная память (исключаяя HMA, с которой можно работать и в 8086™ Real Address Mode). 32-битная адресация становится доступной, начиная с процессоров с архитектурой IA-32. Таким образом, минимум придётся использовать ЦП Intel® 80386™, с которого как раз и началась IA-32.

Теоретически ЦП Intel® 80386™ поддерживает максимум 4 Гб памяти ОЗУ, хотя её реальное значение может быть другим. Диапазон физических адресов, который будет поддерживать драйвер, таков: [10FFF016; FFFFFFFF16], так как 232 - 1 = 4294967295 = FFFFFFFF16. Значит, объём такой памяти будет составлять следующее значение: FFFFFFFF16 - 10FFF016 + 1 = 4293853200 байт = (3 + 67039233 / 226) Гб » 3.99896242 Гб.

Исполнительный файл драйвера будет откомпилирован в ".com"-формат, и резидентная часть будет обслуживать прерывание 24, через которое впоследствии любая 16-разрядная программа сможет получать доступ к XMS-памяти, имея возможность производить операции чтения-записи в ней. Работа драйвера должна быть "прозрачна" для любого 16-разрядного модуля, то есть после обращения к драйверу программа как работала в 8086™ Real Address Mode, так она и должна работать в этом режиме ЦП. Таким образом, работа драйвера заключается в том, что он при передаче ему управления временно переводит процессор в Protected Virtual Address Mode, получая доступ к XMS-памяти, производит операцию чтения или записи, если того требует 16-разрядная программа, а затем перезагружает микропроцессор, тем самым, сбрасывает флаг PE, и ЦП снова переходит в 8086™ Real Address Mode. После этого он возвращает управление вызвавшей программе с передачей определённых параметров (код ошибки и прочие данные), и та продолжает свою работу дальше.

Дата: 19:42:21; 22.02.2003.


Я наконец-то создал полную, отлаженную рабочую версию драйвера. Все выявленные конфликты и прочие характеристики программы представлены непосредственно в исходном тексте файла "Fuck_XMS.asm". Драйвер называется "Fuck_XMS", и скомпилирован он как ".com"-файл. Вызываться он может так:


Вообще для нормальной работы драйвера при его запуске требуется ЦП в 8086™ Real Address Mode, хотя он будет оставаться резидентным в памяти ОЗУ и при любом другом режиме работы процессора. При запуске исполнительного файла драйвера его программа проверяет своё присутствие в ОЗУ, поэтому загрузка второй копии резидентного приложения исключена.

Ниже представлен полный исходный текст XMS-драйвера на языке программирования Turbo Assembler [5.0]:
; ***Драйвер "Fuck_XMS"*** ; Описание драйвера ; Данная TSR-программа представляет собой драйвер, управляющий XMS-памятью ; из-под ОС MS-DOS(R). Этот драйвер позволяет любым 16-разрядным модулям ; производить операции чтения-записи в ячейки XMS-памяти ОЗУ в диапазоне ; физических адресов [10FFF0h; FFFFFFFFh], что составляет примерно ; 3.99896242 Гб. При этом работа драйвера остаётся "прозрачной" для любого ; 16-разрядного приложения. ; Доступ к XMS-памяти осуществляется через защищённый режим работы процессора ; (Protected Virtual Address Mode), реализуемый в архитектуре IA-32, начиная с ; ЦП Intel(R) 80386(TM). 16-разрядные же модули предназначены для работы в ; реальном режиме работы процессора (8086(TM) Real Address Mode). Тут-то и ; возникает противоречие. Поэтому принято, что область рабочей оперативной ; памяти для 16-разрядных программ ограничена пространством в 1 Мб. ; Однако в текущем драйвере эта проблема решена: при получении управления он ; временно переводит ЦП в Protected Virtual Address Mode, тем самым получая ; доступ к XMS-памяти, оперирует с данной памятью, а затем, по окончании, он ; перезагружает микропроцессор, и тот автоматически переходит в 8086(TM) Real ; Address Mode. Восстанавливая все не нужные драйверу, но нужные 16-разрядной ; программе регистры, "Fuck_XMS" далее передаёт управление вызвавшей его ; программе, и та продолжает свою работу. ; Таким образом, 16-разрядная программа как работала в 8086(TM) Real Address ; Mode, так и продолжает в нём работать, не замечая никаких изменений в ; системе. Вот почему работа XMS-драйвера "прозрачна" для любого 16-разрядного ; модуля. ; Как работать с драйвером "Fuck_XMS"? ; TSR-программа "Fuck_XMS.com" "садится" на прерывание 18h (оно может ; перезагружать ЭВМ). Вызов идёт по ассемблерной команде "Int 18h". Обращения ; к себе обработчик прерывания идентифицирует по содержимому регистра AH: если ; AH = FFh, то обращение идёт к данному драйверу, иначе управление передаётся ; исходному обработчику прерывания. Кроме того, задаются дополнительные ; параметры. Итак, формат вызова драйвера "Fuck_XMS" таков (через регистры ; ЦП): ; 1) AH = FFh; ; 2) AL = 0 (при чтении XMS-памяти); ; 3) AL = 1 (при записи в XMS-память); ; 4) (BX + 65536 * CX) - 32-разрядное смещение ячейки XMS-памяти для ; чтения-записи, где BX - младшее слово смещения, CX - старшее слово смещения. ; 32-разрядное смещение задаётся в диапазоне целых чисел [0; 4293853199], ; поскольку размер XMS-памяти для драйвера "Fuck_XMS" примерно равен ; 3.99896242 Гб. ; 4) DL - источник / приёмник значения байта из XMS-памяти: при операции ; чтения XMS-памяти DL возвращает значение ячейки памяти в виде байта по ; указанному смещению; при операции записи содержимое DL, заданное ; 16-разрядной программой, записывается в ячейку XMS-памяти в виде байта. ; Как видно, драйвер работает вопреки традиционной форме: сегмент памяти не ; указывается, поскольку под XMS-память драйвер отводит 1 сегмент размером ; примерно 3.99896242 Гб; задаётся лишь смещение внутри единственного ; сегмента, причём оно - 32-разрядное. ; 16-разрядной программе не следует знать, где физически располагается ; используемая XMS-память, а она её проста должна использовать в своих целях с ; помощью средств, предоставляемых данным драйвером, если ей мало стандартной ; и старшей памяти. ; По окончании работы драйвер возвращает код ошибки 16-разрядному модулю, ; когда флаг CF = 1 для регистра Flags, иначе, при CF = 0, значение регистра ; AL сохраняется исходным (до вызова драйвера). ; Присутствие данного XMS драйвера в памяти ОЗУ проверяется следующим образом: ; 1) AX = -1; ; 2) Int 2Fh. ; Если драйвер "Fuck_XMS" загружен, то возвращается значение AL = 0, иначе ; заданный драйвер отсутствует. ; Коды ошибок ; В процедуре применены все возможные меры, чтобы она не конфликтовала с ; различными резидентными программами, которые уже загружены в память. Это ; очень важное свойство для операционных систем защищённого режима работы ЦП - ; устойчивость к "внешним" воздействиям, - поскольку при нестабильной работе ; программы в PM пользователь может безвозвратно потерять свои несохранённые ; данные на компьютере. Всё же некоторые конфликты процедура имеет. ; Все коды ошибок процедуры см. ниже. ; Коды ошибок: CF = 1 (всегда при наличии ошибки): ; AL = 0 - ЦП Intel(R) 8086(TM); ; AL = 1 - ЦП Intel(R) 80186(TM) или ЦП Intel(R) 80286(TM); ; AL = 2 - ЦП уже находится в Protected Virtual Address Mode; ; AL = 3 - внутренняя неисправимая ошибка программы; ; AL = 4 - батарейка CMOS-микросхемы не работает; ; AL = 5 - невозможно перевести ЦП в Protected Virtual Address Mode; ; AL = 6 - невозможно перевести ЦП в 8086(TM) Real Address Mode; ; AL = 7 - неверный параметр в регистре AL; ; AL = 8 - величина смещения превышает допустимое значение. ; CF = 0 (всегда при отсутствии ошибок) - процедура перехода ЦП в Protected ; Virtual Address Mode и обратно в 8086(TM) Real Address Mode прошла удачно. ; Все прочие программно видимые флаги и регистры ЦП по завершении работы ; приложения остаются в прежнем виде. ; Правила компиляции исходника: ; 1) tasm32.exe Fuck_XMS.asm ; 2) tlink.exe /x /t Fuck_XMS.obj ; 3) del Fuck_XMS.obj ; (файл "Fuck_XMS.asm" - это файл, который содержит текст текущего исходника) ; Формат компиляции: ".com"-файл. ; Конфликты: ; 1) с некоторыми отладчиками, перехватывающими ассемблерную команду ; "out 64h, AL" для перезагрузки ЭВМ (AL - чётное натуральное число в диапазоне ; [240; 254]); ; 2) с некоторыми ОС для ЦП в Protected Virtual Address Mode, которые на стадии ; определения программой режима работы ЦП сами перезагружают ЭВМ (например, ; с программой "Virtual Printer v. 1.0" от Ткачёва С.). ; Размер исполнительного файла: 1677 байт. ; Размер TSR-части в ОЗУ: 1680 байт. ; Требования: ; 1) ЦП Intel(R) 80386(TM) или современнее; ; 2) ЦП в 8086(TM) Real Address Mode. ; Автор: Ефремов А. В. ; Язык программирования: Turbo Assembler 5.0. .386p .MODEL TINY .CODE sta EQU 512; размер стека (в байтах) w EQU 3; количество циклов задержки при паузах ORG 100h Start: jmp NEAR PTR Install TSR_Int18h: pushf cmp AH, 0FFh jne Quit_18h cmp AL, 0 jz Begin cmp AL, 1 je Begin popf mov AL, 7; неверный параметр в регистре AL jmp SHORT Error_exit Quit_18h: popf jmp DWORD PTR [CS:Old24] Overflow_Offset: pop AX popf mov AL, 8; величина смещения превышает допустимое значение jmp SHORT Error_exit i8086: pop AX popf xor AL, AL; ЦП Intel(R) 8086(TM) jmp SHORT Error_exit i80286: pop AX popf mov AL, 1; ЦП Intel(R) 80186(TM) или ЦП Intel(R) 80286(TM) jmp SHORT Error_exit Already_Protected: pop AX popf mov AL, 2; ЦП в Protected Virtual Address Mode jmp SHORT Error_exit Not_CMOS: pop AX popf mov AL, 4; батарейка CMOS-микросхемы не работает jmp SHORT Error_exit Main_Error: pop EBP pop EAX pop AX popf mov AL, 3; ошибка в составлении ".com"-программы Error_exit: pop [CS:IP_x] pop [CS:CS_x] popf stc; CF = 1 (есть ошибка) pushf push [CS:CS_x] push [CS:IP_x] iret; выход из программы Begin: mov [CS:m], AL mov [WORD PTR CS:Offs], BX mov [WORD PTR CS:Offs+2], CX mov [CS:res], DL popf pushf push AX call NEAR PTR No_Trace_&_Intr ; установка нового вектора обработчика прерывания 01h с сохранением старого push ES xor AX, AX mov ES, AX; ES = 0 mov AX, [ES:4] mov [WORD PTR CS:Old01], AX mov AX, [ES:6] mov [WORD PTR CS:Old01+2], AX lea AX, [CS:TSR_Int01h] mov [ES:4], AX mov [ES:6], CS pop ES ; определение ЦП Intel(R) 8086(TM) pushf pop AX and AH, 0Fh push AX popf pushf pop AX and AH, 0F0h cmp AH, 0F0h je i8086 ; определение ЦП Intel(R) 80186(TM) или ЦП Intel(R) 80286(TM) mov AH, 0F0h push AX popf pushf pop AX and AH, 0F0h cmp AH, 0 jz i80286 ; определение режима работы ЦП smsw AX test AL, 1 jne Already_Protected ; проверка батарейки CMOS-микросхемы cli; запрет прерываний во время важной работы in AL, 70h mov [CS:r], AL call NEAR PTR Wait_this mov AL, 0Dh out 70h, AL call NEAR PTR Wait_this in AL, 71h cmp AL, 128 jb Not_CMOS cmp [CS:Offs], 0FFEF000Fh ja Overflow_Offset lea AX, [CS:stack_size] push BX lea BX, [CS:gdt_null] sub AX, BX pop BX mov [CS:gdt_data.limit], AX lea AX, [CS:gdt_null-1] mov [CS:gdt_code.limit], AX mov [CS:gdt_stack.limit], sta-1 lea AX, [CS:Continue] mov [CS:o1], AX ; Вычисления 32-битных сегментов (все смещения сегментов должны быть кратны ; натуральному числу 16). push EAX push EBP ; 32-битный сегмент данных push BX lea BX, [CS:gdt_null]; первый байт 32-битного сегмента данных call NEAR PTR Convert32 pop BX jnz Main_Error shl EAX, 4 mov EBP, EAX; сохраняем 32-битовый линейный адрес push BX lea BX, [CS:gdt_data] mov [CS:[BX].base_l], AX rol EAX, 16 mov [CS:[BX].base_m], AL pop BX ; 32-битный сегмент кода xor EAX, EAX mov AX, CS shl EAX, 4 push BX lea BX, [CS:gdt_code] mov [CS:[BX].base_l], AX rol EAX, 16 mov [CS:[BX].base_m], AL pop BX ; 32-битный сегмент стека push BX lea BX, [CS:stack_size]; первый байт 32-битного сегмента стека call NEAR PTR Convert32 pop BX jnz Main_Error shl EAX, 4 push BX lea BX, [CS:gdt_stack] mov [CS:[BX].base_l], AX rol EAX, 16 mov [CS:[BX].base_m], AL pop BX mov [DWORD PTR CS:pdescr+2], EBP pop EBP pop EAX pop AX popf ; сохранение всех программно видимых регистров мiра сего для ЦП IA-32 pushfd; сохранение регистра EFlags в стеке push DS; сохранение регистра DS в стеке push ES; сохранение регистра ES в стеке push GS; сохранение регистра GS в стеке push FS; сохранение регистра FS в стеке pushad; сохранение прочих 32-разрядных регистров в стеке mov [CS:SS_x], SS; сохранение регистра SS mov [CS:SP_x], SP; сохранение регистра SP call NEAR PTR No_Trace_&_Intr mov [WORD PTR CS:pdescr], 39 mov AL, 0Fh out 70h, AL call NEAR PTR Wait_this in AL, 71h mov [CS:f], AL mov AX, 40h mov ES, AX; в ES - сегмент данных BIOS mov AX, [ES:67h] mov [WORD PTR CS:Old_r], AX mov AX, [ES:69h] mov [WORD PTR CS:Old_r+2], AX ; установка адреса в сегменте данных BIOS, на который передастся управление ; после перезагрузки микропроцессора (перезагрузка осуществляется с целью ; выхода ЦП из Protected Virtual Address Mode и переход его в 8086(TM) Real ; Address Mode снова) lea AX, [CS:Return] mov [ES:67h], AX mov [ES:69h], CS lgdt [CS:pdescr]; загрузка таблицы глобальных дескрипторов ; переход ЦП в Protected Virtual Address Mode smsw AX or AL, 1; установка бита PE регистра CR0 lmsw AX ; очистка очереди команд в блоке предвыборки команд ЦП и загрузка селектора 16 ; в регистр CS DB 0EAh o1 DW 0 DW 16; CS - это селектор 16 ; загрузка всех остальных селекторов в сегментные регистры для ЦП в PM Continue: mov AX, 8 mov DS, AX; DS - это селектор 8 mov AX, 24 mov SS, AX; SS - это селектор 24 mov AX, 32 mov ES, AX; ES - это селектор 32 mov SP, sta-2; инициализация SP для ЦП в PM ; >>>Ура! Мы теперь на 100% находимся в Protected Virtual Address Mode!<<< call NEAR PTR No_Trace_&_Intr smsw AX test AL, 1 jnz PM mov SP, [CS:SP_x]; восстанавливаем регистр SP для применения стека mov SS, [CS:SS_x]; восстанавливаем регистр SS для применения стека call NEAR PTR Restore_Label popad pop FS pop GS pop ES pop DS popfd mov AL, 5; невозможно перевести ЦП в Protected Virtual Address Mode jmp NEAR PTR Error_exit PM: pushf push AX push EDI mov EDI, [DS:65]; переменная Offs cmp [BYTE PTR DS:64], 0; переменная m jz Read_XMS mov AL, [DS:69]; переменная res mov [ES:[EDI]], AL jmp SHORT Reset_CPU Read_XMS: mov AL, [ES:[EDI]] mov [DS:69], AL; переменная res ; переход ЦП в 8086(TM) Real Address Mode Reset_CPU: pop EDI pop AX popf mov AL, 0Fh out 70h, AL call NEAR PTR Wait_this mov AL, 0Ah out 71h, AL call NEAR PTR Wait_this mov AL, 0FEh out 64h, AL hlt; останов ЦП ; (это обязательная команда в программе, поскольку она ожидает готовности ; микропроцессора после его перезагрузки) ; на метку "Return:" управление передаётся после перезагрузки ЦП Return: mov SP, [CS:SP_x]; восстанавливаем регистр SP для применения стека mov SS, [CS:SS_x]; восстанавливаем регистр SS для применения стека call NEAR PTR No_Trace_&_Intr call NEAR PTR Restore_Label ; восстановление всех программно видимых регистров мiра сего для ЦП IA-32 popad; восстановление прочих 32-разрядных регистров из стека pop FS; восстановление регистра FS из стека pop GS; восстановление регистра GS из стека pop ES; восстановление регистра ES из стека pop DS; восстановление регистра DS из стека popfd; восстановление регистра EFlags из стека ; >>>Ура! Мы теперь снова находимся в 8086(TM) Real Address Mode!<<< push AX smsw AX test AL, 1 pop AX jz RM mov AL, 6; невозможно перевести ЦП в 8086(TM) Real Address Mode jmp NEAR PTR Error_exit RM: cmp [CS:m], 0 jnz Do_finish mov DL, [CS:res] Do_finish: pop [CS:IP_x] pop [CS:CS_x] popf clc; CF = 0 (нет ошибок) pushf push [CS:CS_x] push [CS:IP_x] iret; выход из программы ; подпрограмма восстановления адреса обработчика перезагрузки ЭВМ и обработчика ; прерывания 01h, а также состояния CMOS-микросхемы Restore_Label: cli mov AL, 0Fh out 70h, AL call NEAR PTR Wait_this mov AL, [CS:f] out 71h, AL call NEAR PTR Wait_this mov AL, [CS:r] out 70h, AL; загрузка прежнего регистра в порт 70h call NEAR PTR Wait_this mov AX, 40h mov ES, AX mov AX, [WORD PTR CS:Old_r] mov [ES:67h], AX mov AX, [WORD PTR CS:Old_r+2] mov [ES:69h], AX xor AX, AX mov ES, AX; ES = 0 mov AX, [WORD PTR CS:Old01] mov [ES:4], AX mov AX, [WORD PTR CS:Old01+2] mov [ES:6], AX retn ; подпрограмма сброса флагов IF и TF в регистре Flags No_Trace_&_Intr: pushf pop AX and AH, 0FCh push AX popf retn ; подпрограмма паузы по каналу 1 ; (здесь: она работает в RM и PM и использует стек) Wait_this: push AX push CX mov CX, w Wait_again: in AL, 41h mov AH, AL Wait_else: in AL, 41h cmp AL, AH je Wait_else loop Wait_again pop CX pop AX retn ; подпрограмма преобразования 16-битных адресов в 32-битные ; (здесь: она работает в RM) Convert32: push DX xor EAX, EAX mov AX, CS mov DX, 16 mul DX add AX, BX adc DX, 0 mov BX, 16 div BX cmp DX, 0 pop DX retn ; новый обработчик прерывания 01h TSR_Int01h: iret TSR_Int2Fh: cmp AX, -1 jne Quit_2Fh not AL iret Quit_2Fh: jmp DWORD PTR [CS:Old47] ; общая структура дескриптора сегмента dcr struc limit DW 0; граница (биты 0 - 15) base_l DW 0; база (биты 0 - 15) base_m DB 0; база (биты 16 - 23) attr_1 DB 0; атрибуты 1 attr_2 DB 0; граница (биты 16 - 19) и атрибуты 2 base_h DB 0; база (биты 24 - 31) dcr ENDS nop nop nop nop nop nop nop nop nop nop nop nop nop ; описание всех дескрипторов сегментов gdt_null dcr <0, 0, 0, 0, 0, 0>; селектор 0 gdt_data dcr <0, 0, 0, 92h, 0, 0>; селектор 8 gdt_code dcr <0, 0, 0, 98h, 0, 0>; селектор 16 gdt_stack dcr <0, 0, 0, 92h, 0, 0>; селектор 24 gdt_xms dcr <0FFFFh, 0FFF0h, 10h, 92h, 8Fh, 0>; селектор 32 ; Характеристики селекторов: ; селектор 0 - обязательный нулевой дескриптор; ; селектор 8 - сегмент данных; ; селектор 16 - сегмент команд; ; селектор 24 - сегмент стека; ; селектор 32 - сегмент XMS-памяти. pdescr DF 0; псевдодескриптор (6 байт) ; буфер регистров ЦП SS_x DW 0; буфер регистра SS SP_x DW 0; буфер регистра SP r DB 0; номер регистра порта 70h f DB 0; буфер регистра F CMOS-микросхемы Old_r DD 0; адрес старого обработчика перезагрузки ЭВМ Old01 DD 0; адрес старого обработчика прерывания 01h Old24 DD 0 m DB 0; режим работы обработчика (в PM - через смещение 64) Offs DD 0; в PM к этой переменной - через смещение 65 res DB 0; в PM к этой переменной - через смещение 69 IP_x DW 0 CS_x DW 0 Old47 DD 0 nop nop ; стек программы в режиме ЦП PM stack_size DB sta DUP(0) Install: push AX push BX push ES mov ES, [DS:[2Ch]] mov AH, 49h int 21h mov AX, -1 int 2Fh cmp AL, 0 jz Exists_Already mov AX, 352Fh int 21h mov [WORD PTR CS:Old47], BX mov [WORD PTR CS:Old47+2], ES mov AL, 18h int 21h mov [WORD PTR CS:Old24], BX mov [WORD PTR CS:Old24+2], ES mov AH, 25h lea DX, [CS:TSR_Int18h] int 21h mov AL, 2Fh lea DX, [CS:TSR_Int2Fh] int 21h lea DX, [CS:msg1] call NEAR PTR Output pop ES pop BX pop AX lea DX, [CS:Install] int 27h Exists_Already: pop ES pop BX pop AX lea DX, [CS:msg2] call NEAR PTR Output retn Output: push DX lea DX, [CS:ent] mov AH, 9 int 21h pop DX int 21h lea DX, [CS:ent] int 21h retn msg1 DB 'The XMS driver "Fuck_XMS" has been loaded.', 13, 10 DB 'Author: Efremov A. V.', 13, 10 DB 'Russia, Novosibirsk, 2003$' msg2 DB 'The XMS driver "Fuck_XMS" has already been loaded.$' ent DB 13, 10, '$' END Start

Дата: 09:07:12; 23.02.2003.


Ура! Сегодня в РФ 23 февраля - День защитников Отечества! Хотя моё программирование никак не связано с этим праздником, я в этот день написал первую программу, взаимодействующую с драйвером "Fuck_XMS". Она тестирует всю XMS-память, используемую процессором Intel® 80386™; то есть максимум программа может протестировать до примерно 3.99896242 Гб памяти ОЗУ. Хотя если на текущей ЭВМ установлено меньше памяти по сравнению с заданным значением, то она просканирует только её.

В результате теста выдаётся общее количество памяти ОЗУ, установленное на ЭВМ. Для справки: подобный тест выполняет программа ПЗУ-BIOS при начальном включении ЭВМ или при нажатии кнопки "Reset" на корпусе системного блока ПЭВМ (при условии, что в системных настройках BIOS указан режим полного тестирование ЭВМ, а не быстрый тест). В процессе работы программа взаимодействует с пользователем, задавая ему вопрос "Сколько ячеек памяти пропускать (не тестировать) при сканировании XMS-памяти ОЗУ?". Варианты ответа - это целое число в диапазоне [0; 65534]. Если указать 0, то тест будет самым долгим, но подробным, наиболее надёжным и достоверным; если - 65534, то самым быстрым, но неподробным, не самым надёжным и достоверным. Время тестирование зависит от 2 основных факторов:

  1. от частоты работы микропроцессора;
  2. от реального объёма памяти ОЗУ, присутствующей в ЭВМ.


Программа написана на языке программирования PowerBASIC [2.10f], хотя она может работать и компилироваться и на некоторых других языках программирования, например, на Turbo Basic [1.0]. Итак, исходный текст программы таков:
REM Memory Test ' author: Efremov A. V. ON ERROR GOTO 8 REG 1, -1: CALL INTERRUPT &H2F IF REG(1) <> &HFF00 THEN PRINT "The XMS driver " + CHR$(34) + "Fuck_XMS" + CHR$(34) + " isn't being present in the RAM." GOTO 3 END IF CLS : PRINT "This is a programme, which tests your RAM." PRINT "You can choose a full or a quick test. The full test will check every group" PRINT "of bytes, you will have selected before. The quick test firstly will turn" PRINT "the pre-scanning mode on, where he tests the every first byte from the block PRINT "of 64 Kb." PRINT "The quick test sometimes (but not often) mistakes, checking a memory size." PRINT "The error's probability is about 0,00152664763% only. So, it's a small" PRINT "number (less than 1%), and you can trust to the quick test in many" PRINT "situations." PRINT "But if you know well, how many memory you have in the computer, and your" PRINT "computer's CPU is fast enough, you should choose the full test. But know," PRINT "that it takes much more time, than the quick test does." 7 INPUT "How many bytes does the programme have to skip, while testing"; skip& IF (skip& < 0) OR (skip& > 65534) THEN 9 PRINT "Enter the whole number in the diapason [0; 65534]." GOTO 7 END IF INCR skip& CLS : PRINT "Memory Test" LOCATE 5: PRINT "PA (XMS) = XMS_base + XMS_offset; PA (XMS) - Physical Address of the XMS" PRINT "Full or quick test (F / Q)?"; WHILE (z$ <> "F") AND (z$ <> "Q"): LET z$ = UCASE$(INKEY$): WEND LOCATE , 1: PRINT SPACE$(27); : IF z$ = "F" THEN LET x& = 16: GOTO 12 LOCATE , 1: PRINT "Pre-scanning: wait..." LET bx& = 0: FOR x& = 16 TO 65518: LET cx& = x&: GOSUB 10 PRINT "Done:" + STR$(100 * (x& - 16) \ 65503) + "%."; : LOCATE , 1 IF r% = 1 THEN 11 IF r% = 2 THEN 4 IF r% = 3 THEN 6 NEXT x&: LET x& = 65518: GOTO 12 11 IF x& > 16 THEN DECR x& LOCATE 6, 1: PRINT SPACE$(21): PRINT SPACE$(11) 12 FOR cx& = x& TO 65518: FOR bx& = 0 TO 65535 STEP skip&: GOSUB 2 IF r% = 1 THEN 1 IF r% = 2 THEN 4 IF r% = 3 THEN 6 NEXT bx&, cx& LET cx& = 65519: FOR bx& = 0 TO 15: GOSUB 2 IF r% = 1 THEN 1 IF r% = 2 THEN 4 IF r% = 3 THEN 6 NEXT bx&: PRINT "All 4 Gb of the RAM can be used by the system.": GOTO 3 1 PRINT "Memory size: (10FFF0h + 65536 * " + HEX$(cx&) + "h + " + HEX$(bx&) + "h) bytes." 3 END 4 PRINT "The scanning has been aborted by the user.": GOTO 3 6 PRINT "An error has occured in the XMS driver. Error's code: " + HEX$(REG(1) AND &HFF) + "h." IF (REG(1) AND &HFF) < 9 THEN FOR byte% = 0 TO REG(1) AND &HFF: READ z$: NEXT byte% PRINT z$ + "." END IF GOTO 3 2 LOCATE 3, 1: PRINT "XMS_base: 10FFF0h." LET z$ = HEX$(bx&): LET z$ = STRING$(4 - LEN(z$), 48) + z$ PRINT "XMS_offset: " + HEX$(cx&) + z$ + "h." + SPACE$(8): LOCATE 7 10 LET r% = 0: REG 1, &HFF00: REG 2, bx&: REG 3, cx&: CALL INTERRUPT &H18 IF (REG(0) AND 1) = 1 THEN LET r% = 3: GOTO 5 LET byte% = REG(4) IF byte% = &HFF THEN LET nbyte% = 0 ELSE LET nbyte% = byte% + 1 REG 1, &HFF01: REG 4, nbyte%: CALL INTERRUPT &H18 IF (REG(0) AND 1) = 1 THEN LET r% = 3: GOTO 5 REG 1, &HFF00: CALL INTERRUPT &H18 IF (REG(0) AND 1) = 1 THEN LET r% = 3: GOTO 5 IF REG(4) = byte% THEN LET r% = 1 REG 1, &HFF01: REG 4, byte%: CALL INTERRUPT &H18 IF (REG(0) AND 1) = 1 THEN LET r% = 3 5 IF NOT INKEY$ = "" THEN LET r% = 2 RETURN 8 IF ERL = 7 THEN RESUME 9 PRINT "An error has occured in the programme.": RESUME 3 DATA "You have a CPU Intel(R) 8086(TM)" DATA "You have a CPU Intel(R) 80186(TM) or an Intel(R) 80286(TM)" DATA "The CPU is being already in Protected Virtual Address Mode" DATA "There is an internal XMS driver's incorrigible error" DATA "The CMOS-microscheme's battery doesn't work" DATA "The CPU is unable to be set into Protected Virtual Address Mode" DATA "The CPU is unable to be set into 8086(TM) Real Address Mode" DATA "There is an incorrect parameter in the CPU's register AL" DATA "The offset's value overflows the permissible meaning"

Дата: 08:53:14; 25.02.2003.


Я обновил свой XMS-драйвер, немного перестроив его структуру. Новая версия носит название "Fuck_XMS [1.1]".

Теперь резидентная часть программы занимает 1616 байт памяти ОЗУ вместо прежних 1680 байт. Таким образом, размер стал короче на 1680 - 1616 = 64 (байта). Прирост производительности драйвера составил примерно 3.8%. А сокращение размера TSR-части заключается в том, что та часть, которая в предыдущей версии в резидентном блоке анализировала тип центрального процессора, теперь перенесена в транзитную часть программы, и она находится, следовательно, в блоке установки драйвера. Значит, в новой версии при каждом вызове драйвера сейчас не анализируется процессор. Так как резидентная часть выполняет при каждом вызове меньше машинных команд, то, следовательно, и работает она быстрее (если игнорировать суперскалярность современных процессоров, то скорость увеличена примерно на 4%).

Однако, в любом случае, выполняется тривиальный закон: если что-то где-то уменьшилось, то в другом месте это прибудет (и прибудет с лихвой). Так и здесь. Уменьшилась резидентная часть программы - увеличилась транзитная часть. Причём увеличилась она в несколько раз больше: за счёт выводимых в транзитной части дополнительных системных сообщений для пользователя. Поскольку процессор определяется именно в ней, то теперь при отсутствии требуемого ЦП IA-32 программа выводит соответствующее сообщение об ошибке процессора. Общий размер исполнительного ".com"-файла был равен 1677 байт, а стал 1813 байт.

При упразднении резидентной части, как следствие, уменьшилось количество кодов ошибок, возвращаемых драйвером. Исчезли коды ошибок 0 (AL = 0 - ЦП Intel® 8086™) и 1 (AL = 1 - ЦП Intel® 80186™ или ЦП Intel® 80286™). Значит, коды ошибок начинаются теперь с кода 2 (для совместимости с предыдущей версией XMS-драйвера и с программами, написанными для него).

Исходный текст драйвера "Fuck_XMS [1.1]" представлен ниже:

; ***Драйвер "Fuck_XMS [1.1]"*** ; Описание драйвера ; Данная TSR-программа представляет собой драйвер, управляющий XMS-памятью ; из-под ОС MS-DOS(R). Этот драйвер позволяет любым 16-разрядным модулям ; производить операции чтения-записи в ячейки XMS-памяти ОЗУ в диапазоне ; физических адресов [10FFF0h; FFFFFFFFh], что составляет примерно ; 3.99896242 Гб. При этом работа драйвера остаётся "прозрачной" для любого ; 16-разрядного приложения. ; Доступ к XMS-памяти осуществляется через защищённый режим работы процессора ; (Protected Virtual Address Mode), реализуемый в архитектуре IA-32, начиная с ; ЦП Intel(R) 80386(TM). 16-разрядные же модули предназначены для работы в ; реальном режиме работы процессора (8086(TM) Real Address Mode). Тут-то и ; возникает противоречие. Поэтому принято, что область рабочей оперативной ; памяти для 16-разрядных программ ограничена пространством в 1 Мб. ; Однако в текущем драйвере эта проблема решена: при получении управления он ; временно переводит ЦП в Protected Virtual Address Mode, тем самым получая ; доступ к XMS-памяти, оперирует с данной памятью, а затем, по окончании, он ; перезагружает микропроцессор, и тот автоматически переходит в 8086(TM) Real ; Address Mode. Восстанавливая все не нужные драйверу, но нужные 16-разрядной ; программе регистры, "Fuck_XMS" далее передаёт управление вызвавшей его ; программе, и та продолжает свою работу. ; Таким образом, 16-разрядная программа как работала в 8086(TM) Real Address ; Mode, так и продолжает в нём работать, не замечая никаких изменений в ; системе. Вот почему работа XMS-драйвера "прозрачна" для любого 16-разрядного ; модуля. ; Как работать с драйвером "Fuck_XMS"? ; TSR-программа "Fuck_XMS.com" "садится" на прерывание 18h (оно может ; перезагружать ЭВМ). Вызов идёт по ассемблерной команде "Int 18h". Обращения ; к себе обработчик прерывания идентифицирует по содержимому регистра AH: если ; AH = FFh, то обращение идёт к данному драйверу, иначе управление передаётся ; исходному обработчику прерывания. Кроме того, задаются дополнительные ; параметры. Итак, формат вызова драйвера "Fuck_XMS" таков (через регистры ; ЦП): ; 1) AH = FFh; ; 2) AL = 0 (при чтении XMS-памяти); ; 3) AL = 1 (при записи в XMS-память); ; 4) (BX + 65536 * CX) - 32-разрядное смещение ячейки XMS-памяти для ; чтения-записи, где BX - младшее слово смещения, CX - старшее слово смещения. ; 32-разрядное смещение задаётся в диапазоне целых чисел [0; 4293853199], ; поскольку размер XMS-памяти для драйвера "Fuck_XMS" примерно равен ; 3.99896242 Гб. ; 4) DL - источник / приёмник значения байта из XMS-памяти: при операции ; чтения XMS-памяти DL возвращает значение ячейки памяти в виде байта по ; указанному смещению; при операции записи содержимое DL, заданное ; 16-разрядной программой, записывается в ячейку XMS-памяти в виде байта. ; Как видно, драйвер работает вопреки традиционной форме: сегмент памяти не ; указывается, поскольку под XMS-память драйвер отводит 1 сегмент размером ; примерно 3.99896242 Гб; задаётся лишь смещение внутри единственного ; сегмента, причём оно - 32-разрядное. ; 16-разрядной программе не следует знать, где физически располагается ; используемая XMS-память, а она её проста должна использовать в своих целях с ; помощью средств, предоставляемых данным драйвером, если ей мало стандартной ; и старшей памяти. ; По окончании работы драйвер возвращает код ошибки 16-разрядному модулю, ; когда флаг CF = 1 для регистра Flags, иначе, при CF = 0, значение регистра ; AL сохраняется исходным (до вызова драйвера). ; Присутствие данного XMS драйвера в памяти ОЗУ проверяется следующим образом: ; 1) AX = -1; ; 2) Int 2Fh. ; Если драйвер "Fuck_XMS" загружен, то возвращается значение AL = 0, иначе ; заданный драйвер отсутствует. ; Коды ошибок ; В процедуре применены все возможные меры, чтобы она не конфликтовала с ; различными резидентными программами, которые уже загружены в память. Это ; очень важное свойство для операционных систем защищённого режима работы ЦП - ; устойчивость к "внешним" воздействиям, - поскольку при нестабильной работе ; программы в PM пользователь может безвозвратно потерять свои несохранённые ; данные на компьютере. Всё же некоторые конфликты процедура имеет. ; Все коды ошибок процедуры см. ниже. ; Коды ошибок: CF = 1 (всегда при наличии ошибки): ; AL = 2 - ЦП уже находится в Protected Virtual Address Mode; ; AL = 3 - внутренняя неисправимая ошибка программы; ; AL = 4 - батарейка CMOS-микросхемы не работает; ; AL = 5 - невозможно перевести ЦП в Protected Virtual Address Mode; ; AL = 6 - невозможно перевести ЦП в 8086(TM) Real Address Mode; ; AL = 7 - неверный параметр в регистре AL; ; AL = 8 - величина смещения превышает допустимое значение. ; CF = 0 (всегда при отсутствии ошибок) - процедура перехода ЦП в Protected ; Virtual Address Mode и обратно в 8086(TM) Real Address Mode прошла удачно. ; Все прочие программно видимые флаги и регистры ЦП по завершении работы ; приложения остаются в прежнем виде. ; Правила компиляции исходника: ; 1) tasm32.exe Fuck_XMS.asm ; 2) tlink.exe /x /t Fuck_XMS.obj ; 3) del Fuck_XMS.obj ; (файл "Fuck_XMS.asm" - это файл, который содержит текст текущего исходника) ; Формат компиляции: ".com"-файл. ; Конфликты: ; 1) с некоторыми отладчиками, перехватывающими ассемблерную команду ; "out 64h, AL" для перезагрузки ЭВМ (AL - чётное натуральное число в диапазоне ; [240; 254]); ; 2) с некоторыми ОС для ЦП в Protected Virtual Address Mode, которые на стадии ; определения программой режима работы ЦП сами перезагружают ЭВМ (например, ; с программой "Virtual Printer v. 1.0" от Ткачёва С.). ; Размер исполнительного файла: 1813 байт. ; Размер TSR-части в ОЗУ: 1616 байт. ; Требования: ; 1) ЦП Intel(R) 80386(TM) или современнее; ; 2) ЦП в 8086(TM) Real Address Mode. ; Автор: Ефремов А. В. ; Язык программирования: Turbo Assembler 5.0. .386p .MODEL TINY .CODE sta EQU 512; размер стека (в байтах) w EQU 3; количество циклов задержки при паузах ORG 100h Start: jmp NEAR PTR Install TSR_Int18h: pushf cmp AH, 0FFh jne Quit_18h cmp AL, 0 jz Begin cmp AL, 1 je Begin popf mov AL, 7; неверный параметр в регистре AL jmp SHORT Error_exit Quit_18h: popf jmp DWORD PTR [CS:Old24] Overflow_Offset: pop AX popf mov AL, 8; величина смещения превышает допустимое значение jmp SHORT Error_exit Already_Protected: pop AX popf mov AL, 2; ЦП в Protected Virtual Address Mode jmp SHORT Error_exit Not_CMOS: pop AX popf mov AL, 4; батарейка CMOS-микросхемы не работает jmp SHORT Error_exit Main_Error: pop EBP pop EAX pop AX popf mov AL, 3; ошибка в составлении ".com"-программы Error_exit: pop [CS:IP_x] pop [CS:CS_x] popf stc; CF = 1 (есть ошибка) pushf push [CS:CS_x] push [CS:IP_x] iret; выход из программы Begin: mov [CS:m], AL mov [WORD PTR CS:Offs], BX mov [WORD PTR CS:Offs+2], CX mov [CS:res], DL popf pushf push AX call NEAR PTR No_Trace_&_Intr ; установка нового вектора обработчика прерывания 01h с сохранением старого push ES xor AX, AX mov ES, AX; ES = 0 mov AX, [ES:4] mov [WORD PTR CS:Old01], AX mov AX, [ES:6] mov [WORD PTR CS:Old01+2], AX lea AX, [CS:TSR_Int01h] mov [ES:4], AX mov [ES:6], CS pop ES ; определение режима работы ЦП smsw AX test AL, 1 jne Already_Protected ; проверка батарейки CMOS-микросхемы cli; запрет прерываний во время важной работы in AL, 70h mov [CS:r], AL call NEAR PTR Wait_this mov AL, 0Dh out 70h, AL call NEAR PTR Wait_this in AL, 71h cmp AL, 128 jb Not_CMOS cmp [CS:Offs], 0FFEF000Fh ja Overflow_Offset lea AX, [CS:stack_size] push BX lea BX, [CS:gdt_null] sub AX, BX pop BX mov [CS:gdt_data.limit], AX lea AX, [CS:gdt_null-1] mov [CS:gdt_code.limit], AX mov [CS:gdt_stack.limit], sta-1 lea AX, [CS:Continue] mov [CS:o1], AX ; Вычисления 32-битных сегментов (все смещения сегментов должны быть кратны ; натуральному числу 16). push EAX push EBP ; 32-битный сегмент данных push BX lea BX, [CS:gdt_null]; первый байт 32-битного сегмента данных call NEAR PTR Convert32 pop BX jnz Main_Error shl EAX, 4 mov EBP, EAX; сохраняем 32-битовый линейный адрес push BX lea BX, [CS:gdt_data] mov [CS:[BX].base_l], AX rol EAX, 16 mov [CS:[BX].base_m], AL pop BX ; 32-битный сегмент кода xor EAX, EAX mov AX, CS shl EAX, 4 push BX lea BX, [CS:gdt_code] mov [CS:[BX].base_l], AX rol EAX, 16 mov [CS:[BX].base_m], AL pop BX ; 32-битный сегмент стека push BX lea BX, [CS:stack_size]; первый байт 32-битного сегмента стека call NEAR PTR Convert32 pop BX jnz Main_Error shl EAX, 4 push BX lea BX, [CS:gdt_stack] mov [CS:[BX].base_l], AX rol EAX, 16 mov [CS:[BX].base_m], AL pop BX mov [DWORD PTR CS:pdescr+2], EBP pop EBP pop EAX pop AX popf ; сохранение всех программно видимых регистров мiра сего для ЦП IA-32 pushfd; сохранение регистра EFlags в стеке push DS; сохранение регистра DS в стеке push ES; сохранение регистра ES в стеке push GS; сохранение регистра GS в стеке push FS; сохранение регистра FS в стеке pushad; сохранение прочих 32-разрядных регистров в стеке mov [CS:SS_x], SS; сохранение регистра SS mov [CS:SP_x], SP; сохранение регистра SP call NEAR PTR No_Trace_&_Intr mov [WORD PTR CS:pdescr], 39 mov AL, 0Fh out 70h, AL call NEAR PTR Wait_this in AL, 71h mov [CS:f], AL mov AX, 40h mov ES, AX; в ES - сегмент данных BIOS mov AX, [ES:67h] mov [WORD PTR CS:Old_r], AX mov AX, [ES:69h] mov [WORD PTR CS:Old_r+2], AX ; установка адреса в сегменте данных BIOS, на который передастся управление ; после перезагрузки микропроцессора (перезагрузка осуществляется с целью ; выхода ЦП из Protected Virtual Address Mode и переход его в 8086(TM) Real ; Address Mode снова) lea AX, [CS:Return] mov [ES:67h], AX mov [ES:69h], CS lgdt [CS:pdescr]; загрузка таблицы глобальных дескрипторов ; переход ЦП в Protected Virtual Address Mode smsw AX or AL, 1; установка бита PE регистра CR0 lmsw AX ; очистка очереди команд в блоке предвыборки команд ЦП и загрузка селектора 16 ; в регистр CS DB 0EAh o1 DW 0 DW 16; CS - это селектор 16 ; загрузка всех остальных селекторов в сегментные регистры для ЦП в PM Continue: mov AX, 8 mov DS, AX; DS - это селектор 8 mov AX, 24 mov SS, AX; SS - это селектор 24 mov AX, 32 mov ES, AX; ES - это селектор 32 mov SP, sta-2; инициализация SP для ЦП в PM ; >>>Ура! Мы теперь на 100% находимся в Protected Virtual Address Mode!<<< call NEAR PTR No_Trace_&_Intr smsw AX test AL, 1 jnz PM mov SP, [CS:SP_x]; восстанавливаем регистр SP для применения стека mov SS, [CS:SS_x]; восстанавливаем регистр SS для применения стека call NEAR PTR Restore_Label popad pop FS pop GS pop ES pop DS popfd mov AL, 5; невозможно перевести ЦП в Protected Virtual Address Mode jmp NEAR PTR Error_exit PM: pushf push AX push EDI mov EDI, [DS:65]; переменная Offs cmp [BYTE PTR DS:64], 0; переменная m jz Read_XMS mov AL, [DS:69]; переменная res mov [ES:[EDI]], AL jmp SHORT Reset_CPU Read_XMS: mov AL, [ES:[EDI]] mov [DS:69], AL; переменная res ; переход ЦП в 8086(TM) Real Address Mode Reset_CPU: pop EDI pop AX popf mov AL, 0Fh out 70h, AL call NEAR PTR Wait_this mov AL, 0Ah out 71h, AL call NEAR PTR Wait_this mov AL, 0FEh out 64h, AL hlt; останов ЦП ; (это обязательная команда в программе, поскольку она ожидает готовности ; микропроцессора после его перезагрузки) ; на метку "Return:" управление передаётся после перезагрузки ЦП Return: mov SP, [CS:SP_x]; восстанавливаем регистр SP для применения стека mov SS, [CS:SS_x]; восстанавливаем регистр SS для применения стека call NEAR PTR No_Trace_&_Intr call NEAR PTR Restore_Label ; восстановление всех программно видимых регистров мiра сего для ЦП IA-32 popad; восстановление прочих 32-разрядных регистров из стека pop FS; восстановление регистра FS из стека pop GS; восстановление регистра GS из стека pop ES; восстановление регистра ES из стека pop DS; восстановление регистра DS из стека popfd; восстановление регистра EFlags из стека ; >>>Ура! Мы теперь снова находимся в 8086(TM) Real Address Mode!<<< push AX smsw AX test AL, 1 pop AX jz RM mov AL, 6; невозможно перевести ЦП в 8086(TM) Real Address Mode jmp NEAR PTR Error_exit RM: cmp [CS:m], 0 jnz Do_finish mov DL, [CS:res] Do_finish: pop [CS:IP_x] pop [CS:CS_x] popf clc; CF = 0 (нет ошибок) pushf push [CS:CS_x] push [CS:IP_x] iret; выход из программы ; подпрограмма восстановления адреса обработчика перезагрузки ЭВМ и обработчика ; прерывания 01h, а также состояния CMOS-микросхемы Restore_Label: cli mov AL, 0Fh out 70h, AL call NEAR PTR Wait_this mov AL, [CS:f] out 71h, AL call NEAR PTR Wait_this mov AL, [CS:r] out 70h, AL; загрузка прежнего регистра в порт 70h call NEAR PTR Wait_this mov AX, 40h mov ES, AX mov AX, [WORD PTR CS:Old_r] mov [ES:67h], AX mov AX, [WORD PTR CS:Old_r+2] mov [ES:69h], AX xor AX, AX mov ES, AX; ES = 0 mov AX, [WORD PTR CS:Old01] mov [ES:4], AX mov AX, [WORD PTR CS:Old01+2] mov [ES:6], AX retn ; подпрограмма сброса флагов IF и TF в регистре Flags No_Trace_&_Intr: pushf pop AX and AH, 0FCh push AX popf retn ; подпрограмма паузы по каналу 1 ; (здесь: она работает в RM и PM и использует стек) Wait_this: push AX push CX mov CX, w Wait_again: in AL, 41h mov AH, AL Wait_else: in AL, 41h cmp AL, AH je Wait_else loop Wait_again pop CX pop AX retn ; подпрограмма преобразования 16-битных адресов в 32-битные ; (здесь: она работает в RM) Convert32: push DX xor EAX, EAX mov AX, CS mov DX, 16 mul DX add AX, BX adc DX, 0 mov BX, 16 div BX cmp DX, 0 pop DX retn ; новый обработчик прерывания 01h TSR_Int01h: iret TSR_Int2Fh: cmp AX, -1 jne Quit_2Fh not AL iret Quit_2Fh: jmp DWORD PTR [CS:Old47] ; общая структура дескриптора сегмента dcr struc limit DW 0; граница (биты 0 - 15) base_l DW 0; база (биты 0 - 15) base_m DB 0; база (биты 16 - 23) attr_1 DB 0; атрибуты 1 attr_2 DB 0; граница (биты 16 - 19) и атрибуты 2 base_h DB 0; база (биты 24 - 31) dcr ENDS ; описание всех дескрипторов сегментов gdt_null dcr <0, 0, 0, 0, 0, 0>; селектор 0 gdt_data dcr <0, 0, 0, 92h, 0, 0>; селектор 8 gdt_code dcr <0, 0, 0, 98h, 0, 0>; селектор 16 gdt_stack dcr <0, 0, 0, 92h, 0, 0>; селектор 24 gdt_xms dcr <0FFFFh, 0FFF0h, 10h, 92h, 8Fh, 0>; селектор 32 ; Характеристики селекторов: ; селектор 0 - обязательный нулевой дескриптор; ; селектор 8 - сегмент данных; ; селектор 16 - сегмент команд; ; селектор 24 - сегмент стека; ; селектор 32 - сегмент XMS-памяти. pdescr DF 0; псевдодескриптор (6 байт) ; буфер регистров ЦП SS_x DW 0; буфер регистра SS SP_x DW 0; буфер регистра SP r DB 0; номер регистра порта 70h f DB 0; буфер регистра F CMOS-микросхемы Old_r DD 0; адрес старого обработчика перезагрузки ЭВМ Old01 DD 0; адрес старого обработчика прерывания 01h Old24 DD 0 m DB 0; режим работы обработчика (в PM - через смещение 64) Offs DD 0; в PM к этой переменной - через смещение 65 res DB 0; в PM к этой переменной - через смещение 69 IP_x DW 0 CS_x DW 0 Old47 DD 0 nop nop ; стек программы в режиме ЦП PM stack_size DB sta DUP(0) Install: push AX push BX push ES mov ES, [DS:[2Ch]] mov AH, 49h int 21h ; определение ЦП Intel(R) 8086(TM) pushf pop AX and AH, 0Fh push AX popf pushf pop AX and AH, 0F0h cmp AH, 0F0h je i8086 ; определение ЦП Intel(R) 80186(TM) или ЦП Intel(R) 80286(TM) mov AH, 0F0h push AX popf pushf pop AX and AH, 0F0h cmp AH, 0 jz i80286 mov AX, -1 int 2Fh cmp AL, 0 jz Exists_Already mov AX, 352Fh int 21h mov [WORD PTR CS:Old47], BX mov [WORD PTR CS:Old47+2], ES mov AL, 18h int 21h mov [WORD PTR CS:Old24], BX mov [WORD PTR CS:Old24+2], ES mov AH, 25h lea DX, [CS:TSR_Int18h] int 21h mov AL, 2Fh lea DX, [CS:TSR_Int2Fh] int 21h lea DX, [CS:msg1] call NEAR PTR Output pop ES pop BX pop AX lea DX, [CS:Install] int 27h i8086: pop ES pop BX pop AX lea DX, [CS:m8086] jmp SHORT Exit i80286: pop ES pop BX pop AX lea DX, [CS:m80286] jmp SHORT Exit Exists_Already: pop ES pop BX pop AX lea DX, [CS:msg2] Exit: call NEAR PTR Output retn Output: push DX lea DX, [CS:ent] mov AH, 9 int 21h pop DX int 21h lea DX, [CS:ent] int 21h retn msg1 DB 'The XMS driver "Fuck_XMS [1.1]" has been loaded.', 13, 10 DB 'Author: Efremov A. V.', 13, 10 DB 'Russia, Novosibirsk, 2003$' m8086 DB 'You have an Intel(R) 8086(TM), but it is needed an IA-32.$' m80286 DB 'You have an Intel(R) 80186(TM) or an Intel(R) 80286(TM), but it is' DB ' needed an', 13, 10, 'IA-32.$' msg2 DB 'The XMS driver "Fuck_XMS" has already been loaded.$' ent DB 13, 10, '$' END Start

Дата: 08:30:42; 21.06.2003.


Я написал резидентную программу, взаимодействующую с драйвером "Fuck_XMS". Её код является 32-разрядным, поэтому он предназначен для выполнения на микропроцессорах с архитектурой IA-32, то есть начиная с ЦП Intel® 80386™. Называется программа "Реаниматор ОС DOS®". Её целью поставлено полное восстановление операционной системы типа DOS® в памяти ОЗУ при различных сбоях, отказах в работе системы, зависании программ и т. д.

Идея решения данной задачи идеально проста: данная TSR-программа сохраняет образ областей памяти - стандартную память (conventional memory), старшую память (upper memory) и HMA-память - в XMS-памяти, пользуясь средствами, предоставляемыми драйвером "Fuck_XMS", а затем, если программа или пользователь требует, восстанавливает указанные области памяти по сохранённому образу из XMS-памяти.

Для своей работы данное приложение определяет начальный физический адрес XMS-памяти, куда будут производиться операции чтения-записи. Само число является константой (зарезервированным), и прописано оно в исходнике программы. Программа пишет, начиная с физического адреса 22FFF016 и кончая адресом 33FFDF16, то есть диапазон используемых физических адресов памяти ОЗУ таков: [22FFF016; 33FFDF16]. Как видно, для работы программы требуется плата ОЗУ, имеющая минимальный объём в 3407840 байт, что составляет примерно 3.24996948 Мб. Если памяти на плате ОЗУ будет недостаточно, программа не останется резидентной.

Естественно, в ту область XMS-памяти, где находится образ ОС, никакая другая программа не должна ничего прописывать, иначе впоследствии восстановятся искажённые данные.

Так как программа пользуется услугами драйвера "Fuck_XMS", то сама она работает в реальном режиме работы ЦП.

При первом запуске приложение сохраняет автоматически образ областей памяти и далее передаёт управление вызвавшей её программе, оставаясь резидентной. Скорость сохранения / восстановления областей памяти зависит от тактовой частоты работы микропроцессора: чем она выше, тем быстрее работа программы.

Ниже представлен исходный код программы на языке программирования Turbo Assembler [5.0]:

; ***Реаниматор ОС DOS(R)*** ; Программа сохраняет / восстанавливает образ стандартной, старшей и HMA-памяти ; ОЗУ, а также регистров ЦП. ; ~ Стандартная память (conventional memory) - это первые 640 Кб физической ; памяти ОЗУ. ; ~ Старшая память (upper memory) - это последующие 384 Кб физической ; памяти ОЗУ, которые идут за стандартной памятью. ; ~ HMA-память (high memory area, или область старшей памяти) - это ; подразделение XMS-памяти (extended memory specification), которое начинается ; с физического адреса 10FFF0h и занимает 65520 байт. (Для справки: управлением ; HMA-памяти в ОС DOS(R) занимается драйвер "HiMem.sys".) ; ~ XMS-память (extended memory specification, или расширенная память) - это ; область физической памяти ОЗУ, которая имеет максимальный размер 4 Гб и ; начинается с физического адреса 10FFF0h. ; Сохранение и восстановление образа указанных областей памяти ОЗУ идёт с ; использованием XMS-памяти при обязательном участии драйвера XMS-памяти ; "Fuck_XMS". ; Код данной программы является 32-разрядным, поэтому для неё требуется минимум ; ЦП Intel(R) 80386(TM) или совместимый (поддерживающий архитектуру IA-32). ; В каком бы состоянии ни находилась ОС DOS(R), программа сможет её ; "воскресить" по заранее сохранённому образу при следующих условиях: ; 1) система реагирует на нажатия клавиш на клавиатуре, и обработчик TSR-части ; данной программы вызывается; ; 2) машинный код TSR-части данной программы не видоизменён в памяти ОЗУ; ; 3) машинный код TSR-части драйвера "Fuck_XMS" не видоизменён в памяти ОЗУ; ; 4) никакая программа не прописывала в XMS-память, где находится образ, ; какие-либо значения, тем самым видоизменяя структуру самого образа; ; 5) сегменты стека не указывают на сегменты кодов TSR-частей данной программы ; или драйвера "Fuck_XMS", иначе стек может затереть код; ; 6) в процессе работы программа на определённом этапе использует свой ; внутрениий стек, находящийся в сегменте кода данной TSR-программы: она ; вызывает в это время обработчики прерываний, и те также используют её стек; ; если обработчиков достаточно много, и они активно используют стек данной ; TSR-программы, то стек будет переполнен, и он "наедет" на машинные коды ; и, следовательно, сотрёт их. Это приведёт к зависанию системы либо к её ; непредсказуемой работе. Чем программный стек больше, тем меньше вероятность ; того, что он будет быстро переполнен, а, следовательно, тем стабильнее работа ; данной TSR-программы. Однако чем больше стек, тем больше памяти ОЗУ занимает ; резидент и, значит, тем меньше свободной памяти. ; Описанные выше 6 случаев происходят довольно редко (может, кроме случая 1)), ; поэтому программа остаётся работоспособной и имеет возможность восстановить ; ОС. ; Так как программа работает с XMS-памятью, начиная с физического адреса ; PA = 65536 * begin_h + begin_l + 10FFF0h = 65536 * 18 + 0 + 10FFF0h = 2293744 ; = 22FFF0h, и ей требуется сохранить 10h * FFFFh + FFFFh + 1 = 10FFF0h байт = ; 1114096 байт, то, следовательно, ей понадобится минимум 22FFF0h + 1114096 = ; 3407840 байт. Итак, для работы данной программы требуется минимум ; около 3.25 Мб памяти ОЗУ, или, если быть точным, 3407840 байт оперативной ; памяти. ; Обратите внимание: программа сохраняет / восстанавливает состояние ячеек ; памяти ОЗУ и программно-видимых регистров ЦП, однако она не предусмотрена для ; сохранения / восстановления состояния внешних устройств ввода-вывода. Внешние ; устройства будут находиться в том состоянии, в котором они остались после ; сбоя операционной системы или после каких-либо иных случаях. (Автор всё же ; предусмотрел в программе попытку восстановления работоспособности таких ; внешних устройств как AT-клавиатура и монитор). ; ; ~~ Техническая информация для программистов: ; программа перехватывает 3 вектора прерывания: 09h, 18h и 2Fh. ; По вектору 09h программа перехватывает нажатие комбинаций "<LCtrl> + <RCtrl> ; + <S>" и "<LCtrl> + <RCtrl> + <L>". Первая комбинация сохраняет образ памяти, ; вторая - его восстанавливает. ; Аналогичные действия клавиатурным комбинациям могут выполнять приложения, ; используя вызов прерывания 18h. Предварительно следует записать параметры в ; регистр AX: ; 1) AX = FE00h (при восстановлении образа памяти ОЗУ из XMS-памяти); ; 2) AX = FE01h (при сохранении образа памяти ОЗУ в XMS-памяти); ; 3) int 18h. ; При AX = FE00h вызов прерывания 18h подобен нажатию комбинации "<LCtrl> + ; <RCtrl> + <L>"; при AX = FE01h, - соответственно, комбинации "<LCtrl> + ; <RCtrl> + <S>". ; Перед оставлением резидентной части программа предварительно анализирует ; присутствие своей предыдущей копии в памяти ОЗУ. Если копия обнаружена, ; программа не остаётся повторно резидентной. Так можно определить, загружен ли ; данный резидент в память ОЗУ или ещё нет. Всякое приложение также может ; определить её присутствие, воспользовавшись прерыванием 2Fh. Формат его ; вызова таков: ; 1) AX = FEFFh; ; 2) int 2Fh ; 3) возвращает: AL = 0, если данный резидент присутствует в памяти ОЗУ; иначе ; он отсутствует. ; Формат компиляции: ".com"-файл. ; Конфликты: ; 1) с программами, прописывающими в XMS-память, где есть образ памяти ОЗУ; ; 2) см. документацию на драйвер "Fuck_XMS". ; Размер исполнительного файла: 6618 байт. ; Размер TSR-части в ОЗУ: 5728 байт. ; Требования: ; 1) ЦП Intel(R) 80386(TM) или современнее; ; 2) цветной EGA-совместимый видеоадаптер; ; 3) плата ОЗУ с минимальным объёмом 3407840 байт; ; 4) AT-клавиатура; ; 5) режим эмуляции ОС DOS(R). ; Рекомендации: ; 1) не применять совместно с программами, использующими XMS-память для записи ; в области адресов, где данная TSR-программа хранит образ памяти ОЗУ; ; 2) не использовать с программами, переводящими ЦП в Protected Virtual Address ; Mode, кроме как с драйвером "Fuck_XMS", на который есть точная документация; ; 3) если данная TSR-программа при восстановлении образа зависает либо ; производит непредсказуемую работу, увеличьте в исходнике размер её ; программного стека и перекомпилируйте сам исходник; ; 4) сохраняйте образ системы перед продолжением работы, используя эту ; программу, чтобы впоследствии при сбоях его можно было бы восстановить. ; Автор: Ефремов А. В. ; Язык программирования: Turbo Assembler 5.0. .386p .MODEL TINY .CODE stack_size EQU 2048; stack's size (in words) sym EQU 51; text's length (in bytes) c EQU 14; colour's code of a text begin_h EQU 18; high word of the XMS 32-bit offset begin_l EQU 0; low word of the XMS 32-bit offset ORG 100h Start: jmp NEAR PTR Install stack_hr DW stack_size DUP(257); program's stack TSR_Int18h: cmp AX, 0FE00h je Do_this_18h cmp AX, 0FE01h je Do_this_18h jmp DWORD PTR [CS:Old24] Do_this_18h: call NEAR PTR Test_XMS_drv jc Skip_restore_registers mov [CS:s], AL; remembering mode in the [CS:s] ; saving the real stack's addreess mov [CS:SS_v], SS mov [CS:SP_v], SP cmp [CS:s], 1 jne Skip_save_registers mov [CS:EAX_x], EAX mov [CS:EBX_x], EBX mov [CS:ECX_x], ECX mov [CS:EDX_x], EDX mov [CS:ESI_x], ESI mov [CS:EDI_x], EDI mov [CS:EBP_x], EBP mov [CS:ESP_x], ESP mov [CS:DS_x], DS mov [CS:ES_x], ES mov [CS:SS_x], SS mov [CS:FS_x], FS mov [CS:GS_x], GS push DS push ES push SI push DI push CS pop ES; ES = CS lea DI, [CS:buf1] mov SI, 0B800h mov DS, SI; DS = B800h xor SI, SI call NEAR PTR Read_Write pop DI pop SI pop ES pop DS push ES push AX mov AX, 40h mov ES, AX; ES = 40h mov AL, [ES:[49h]] mov [CS:scr], AL mov AL, [ES:[17h]] mov [CS:key], AL pop AX pop ES ; creating our own stack Skip_save_registers: push CS pop SS; SS = CS lea SP, [CS:TSR_Int18h-1] cmp [CS:s], 0 jnz Skip_set_screen_&_AT_keyboard push AX pushf xor AH, AH; AH = 0 mov AL, [CS:scr]; AL = [CS:scr] pushf call DWORD PTR [CS:Old16] popf mov AL, 0F4h; turn on an AT-keyboard call NEAR PTR Send_to_i8042 mov AL, 0EDh; controlling the lightdiods of an AT-keyboard call NEAR PTR Send_to_i8042 mov AH, [CS:key] xor AL, AL; AL = 0 test AH, 16 jz Skip_Scroll_Lock_set inc AL Skip_Scroll_Lock_set: test AH, 32 jz Skip_Num_Lock_set add AL, 2 Skip_Num_Lock_set: test AH, 64 jz Skip_Caps_Lock_set add AL, 4 Skip_Caps_Lock_set: call NEAR PTR Send_to_i8042 pop AX Skip_set_screen_&_AT_keyboard: pushf push BP push BX push CX push DI mov CX, begin_h mov BX, begin_l sub BX, 1 sbb CX, 0 call NEAR PTR No_Trace_and_Interruptions mov BP, 0F000h Increase_segment: add BP, 1000h mov DI, -1 call NEAR PTR Do_save_or_load_proc cmp BP, 0F000h jb Increase_segment ; turn the line A20 on push AX mov AL, 0DFh call NEAR PTR On_Off_A20 pop AX mov BP, 0FFFFh mov DI, 15 call NEAR PTR Do_save_or_load_proc ; turn the line A20 off push AX mov AL, 0DDh call NEAR PTR On_Off_A20 pop AX pop DI pop CX pop BX pop BP popf ; restoring the real stack's address mov SS, [CS:SS_v] mov SP, [CS:SP_v] cmp [CS:s], 0 jnz Skip_restore_registers mov EAX, [CS:EAX_x] mov EBX, [CS:EBX_x] mov ECX, [CS:ECX_x] mov EDX, [CS:EDX_x] mov ESI, [CS:ESI_x] mov EDI, [CS:EDI_x] mov EBP, [CS:EBP_x] mov ESP, [CS:ESP_x] mov DS, [CS:DS_x] mov ES, [CS:ES_x] mov SS, [CS:SS_x] mov FS, [CS:FS_x] mov GS, [CS:GS_x] push DS push ES push SI push DI push CS pop DS; DS = CS lea SI, [CS:buf1] mov DI, 0B800h mov ES, DI; ES = B800h xor DI, DI call NEAR PTR Read_Write pop DI pop SI pop ES pop DS call NEAR PTR Activate_the_PBI Skip_restore_registers: iret TSR_Int2Fh: cmp AX, 0FEFFh jne Quit_2Fh not AL iret Quit_2Fh: jmp DWORD PTR [CS:Old47] TSR_Int09h: push AX push ES xor AX, AX mov ES, AX; ES = 0 call NEAR PTR Wait_i8042 in AL, 60h test [BYTE PTR [ES:1047]], 4 jz Quit_09h test [BYTE PTR [ES:1048]], 1 jz Quit_09h test [BYTE PTR [ES:1174]], 4 jz Quit_09h cmp AL, 31; клавиша "S"? je Save_RAM cmp AL, 38; клавиша "L"? je Load_RAM Quit_09h: pop ES pop AX jmp DWORD PTR [CS:Old09] Load_RAM: mov AL, 0 jmp SHORT Call_XMS_drv Save_RAM: mov AL, 1 Call_XMS_drv: mov AH, 0FEh pushf call DWORD PTR [CS:Old24_n] and [BYTE PTR ES:1047], 255-4 and [BYTE PTR ES:1048], 255-1 and [BYTE PTR ES:1174], 255-4 call NEAR PTR Activate_the_PBI pop ES pop AX iret Activate_the_PBI: push AX mov AL, 20h out 20h, AL; EOI on the PBI-1 out 0A0h, AL; EOI on the PBI-2 pop AX retn Do_save_or_load_proc: pushf push DI Increment_offset: inc DI add BX, 1 adc CX, 0 push SI lea SI, [CS:this_a] call NEAR PTR Count_PA pop SI push EAX mov EAX, [CS:this_a] cmp EAX, [CS:min_a] jb Continue_18h cmp EAX, [CS:max_a] ja Continue_18h pop EAX jmp SHORT Skip_continue_18h Continue_18h: pop EAX push BX push DI mov BX, DI; BX = DI lea DI, [CS:msg1+(sym-4)] call NEAR PTR Convert_hex mov BX, BP; BX = BP lea DI, [CS:msg1+(sym-10)] call NEAR PTR Convert_hex pop DI pop BX push DS push ES push SI push DI push CS pop ES; ES = CS mov SI, 0B800h mov DS, SI; DS = B800h lea DI, [CS:buf] xor SI, SI call NEAR PTR Read_Write lea SI, [CS:msg1] call NEAR PTR Output_Msg pop DI pop SI pop ES pop DS push ES push AX push DX push BP pop ES; ES = BP mov DL, [ES:[DI]] mov AH, 0FFh mov AL, [CS:s] pushf call DWORD PTR [CS:Old24] call NEAR PTR No_Trace_and_Interruptions jnc No_errors_in_XMS_driver push SI lea SI, [CS:msg2] call NEAR PTR Output_Msg pop SI hlt No_errors_in_XMS_driver: cmp [CS:s], 0 jnz Skip_restore_RAM mov [ES:[DI]], DL Skip_restore_RAM: pop DX pop AX pop ES push DS push ES push SI push DI push CS pop DS; DS = CS lea SI, [CS:buf] mov DI, 0B800h mov ES, DI; ES = B800h xor DI, DI call NEAR PTR Read_Write pop DI pop SI pop ES pop DS Skip_continue_18h: cmp DI, 0FFFFh jb Increment_offset pop DI popf retn ; counts a physical address ; (incoming data: BP - segment; DI - offset; SI - offset for writing in a ; double word) Count_PA: pushf push AX push DX call NEAR PTR No_Trace_and_Interruptions mov AX, 10h mov DX, BP mul DX add AX, DI adc DX, 0 mov [WORD PTR CS:[SI]], AX mov [WORD PTR CS:[SI]+2], DX pop DX pop AX popf retn No_Trace_and_Interruptions: push AX pushf pop AX and AH, 0FCh push AX popf pop AX retn Read_Write: pushf push AX push CX call NEAR PTR No_Trace_and_Interruptions cld mov CX, sym R_W: lodsw stosw loop R_W pop CX pop AX popf retn Output_Msg: pushf push ES push DS push SI push DI push AX push CX call NEAR PTR No_Trace_and_Interruptions cld push CS pop DS; DS = CS mov AX, 0B800h mov ES, AX; ES = B800h xor DI, DI mov CX, sym mov AH, c Read_Write_text: lodsb stosw loop Read_Write_text pop CX pop AX pop DI pop SI pop DS pop ES popf retn ; converts a machine number into the hexadecimal string ; (incoming data: BX - a machine number; DI - an offset of the last digit of ; the future hexadecimal string) Convert_hex: pushf push ES push DS push CX push DX push AX push BX push DI call NEAR PTR No_Trace_and_Interruptions std push CS pop ES; ES = CS push CS pop DS; DS = CS mov CX, 4; 4 hexadecimal digits Next_convert_hex: xor DX, DX; DX = 0 mov AX, BX; AX = BX mov BX, 10h div BX mov BX, AX push BX lea BX, [CS:tbl] mov AL, DL xlat stosb pop BX loop Next_convert_hex pop DI pop BX pop AX pop DX pop CX pop DS pop ES popf retn On_Off_A20: pushf call NEAR PTR No_Trace_and_Interruptions call NEAR PTR Wait_i8042 push AX mov AL, 0D1h out 64h, AL pop AX call NEAR PTR Send_to_i8042 push AX mov AL, 0FFh out 64h, AL pop AX call NEAR PTR Wait_i8042 popf retn Send_to_i8042: pushf call NEAR PTR No_Trace_and_Interruptions call NEAR PTR Wait_i8042 out 60h, AL call NEAR PTR Wait_i8042 popf retn Wait_i8042: pushf push AX Wait_i8042_else: in AL, 64h test AL, 2 jnz Wait_i8042_else pop AX popf retn Test_XMS_drv: push AX push BX push CX push DX mov AX, 0FF00h mov BX, begin_l mov CX, begin_h pushf call DWORD PTR [CS:Old24] pop DX pop CX pop BX pop AX retn msg1 DB 'The system is busy. Current address: [0000h:0000h].' msg2 DB 'An error has ocurred in the XMS - system is halted.' buf DW sym DUP(0); screen's buffer buf1 DW sym DUP(0) tbl DB '0123456789ABCDEF' scr DB 0 key DB 0 s DB 0 Old09 DD 0 Old16 DD 0 Old24 DD 0 Old47 DD 0 Old24_n DD 0 min_a DD 0; minimum physical address of the TSR-part max_a DD 0; maximum physical address of the TSR-part this_a DD 0; current physical address of the 8086(TM) Real Address Mode SS_v DW 0 SP_v DW 0 EAX_x DD 0 EBX_x DD 0 ECX_x DD 0 EDX_x DD 0 ESI_x DD 0 EDI_x DD 0 EBP_x DD 0 ESP_x DD 0 DS_x DD 0 ES_x DD 0 SS_x DD 0 FS_x DD 0 GS_x DD 0 Install: push AX push BX push ES mov ES, [DS:[2Ch]] mov AH, 49h int 21h mov AX, 1600h int 2Fh cmp AL, 0 jz No_MS_Windows cmp AL, 80h jne MS_Windows No_MS_Windows: pushf call NEAR PTR No_Trace_and_Interruptions mov DX, 3D4h mov AL, 0Fh out DX, AL inc DL in AL, DX push AX mov AL, 100 out DX, AL pop AX out DX, AL in AL, DX cmp AL, 0FFh je Not_EGA ; определение ЦП Intel(R) 8086(TM) pushf pop AX and AH, 0Fh push AX popf pushf pop AX and AH, 0F0h cmp AH, 0F0h je i8086 ; определение ЦП Intel(R) 80186(TM) или ЦП Intel(R) 80286(TM) mov AH, 0F0h push AX popf pushf pop AX and AH, 0F0h cmp AH, 0 jz i80286 mov AL, 0Dh out 70h, AL in AL, 71h popf cmp AL, 80h jb Not_CMOS push ES push CX xor AX, AX mov ES, AX; ES = 0 mov CX, 91; time of the wait: (CX / 18.2065) seconds mov AL, 0FFh call NEAR PTR Send_to_i8042 Wait_k_1: mov AH, [ES:46Ch] Wait_k_2: in AL, 64h cmp AL, 1Ch je Yes_AT_kbd cmp AL, 5Ch je No_AT_kbd cmp [ES:46Ch], AH je Wait_k_2 loop Wait_k_1 No_AT_kbd: pop CX pop ES lea DX, [CS:AT_k] jmp NEAR PTR Print_&_Finish Yes_AT_kbd: pop CX pop ES mov AX, 0FEFFh int 2Fh cmp AL, 0 jz Already_exists mov AX, -1 int 2Fh cmp AL, 0 jnz No_XMS_driver mov AX, 3510h int 21h mov [WORD PTR CS:Old16], BX mov [WORD PTR CS:Old16+2], ES mov AL, 2Fh int 21h mov [WORD PTR CS:Old47], BX mov [WORD PTR CS:Old47+2], ES mov AL, 09h int 21h mov [WORD PTR CS:Old09], BX mov [WORD PTR CS:Old09+2], ES mov AL, 18h int 21h mov [WORD PTR CS:Old24], BX mov [WORD PTR CS:Old24+2], ES mov AH, 25h lea DX, [CS:TSR_Int18h] int 21h mov AL, 09h lea DX, [CS:TSR_Int09h] int 21h mov AL, 2Fh lea DX, [CS:TSR_Int2Fh] int 21h mov AX, 3518h int 21h mov [WORD PTR CS:Old24_n], BX mov [WORD PTR CS:Old24_n+2], ES lea DX, [CS:t] mov AH, 9 int 21h call NEAR PTR Test_XMS_drv jc Not_works lea DX, [CS:noer] int 21h pop ES pop BX pop AX push BP push DI push SI mov BP, CS; BP = CS lea DI, [CS:stack_hr] lea SI, [CS:min_a] call NEAR PTR Count_PA lea DI, [CS:Install-1] lea SI, [CS:max_a] call NEAR PTR Count_PA pop SI pop DI pop BP push AX mov AX, 0FE01h int 18h pop AX lea DX, [CS:Install-1] int 27h MS_Windows: lea DX, [CS:win] jmp SHORT Print_&_Finish Not_EGA: lea DX, [CS:ega] jmp SHORT Print_&_Finish_before i8086: lea DX, [CS:ip8086] jmp SHORT Print_&_Finish_before i80286: lea DX, [CS:ip80286] jmp SHORT Print_&_Finish_before Not_CMOS: lea DX, [CS:cmos] jmp SHORT Print_&_Finish_before Already_exists: lea DX, [CS:alr] jmp SHORT Print_&_Finish No_XMS_driver: lea DX, [CS:xms] Print_&_Finish: call NEAR PTR Entr int 21h Pre_Quit: call NEAR PTR Entr pop ES pop BX pop AX retn Print_&_Finish_before: popf jmp SHORT Print_&_Finish Not_works: lds DX, [CS:Old24] mov AX, 2518h int 21h lds DX, [CS:Old09] mov AL, 09h int 21h lds DX, [CS:Old47] mov AL, 2Fh int 21h push CS pop DS; DS = CS lea DX, [CS:er] mov AH, 9 int 21h jmp SHORT Pre_Quit Entr: push DX lea DX, [CS:ent] mov AH, 9 int 21h pop DX retn t DB 13, 10, 'Testing the XMS driver "Fuck_XMS"... $' noer DB 'No errors.', 13, 10, 'Press "<LCtrl> + <RCtrl> + <S>" to save an ' DB 'image into the XMS, "<LCtrl> +', 13, 10, '<RCtrl> + <L>" to load it from ' DB 'the XMS.', 13, 10 DB 'Author: Efremov A. V. (C)', 13, 10, 'Russia, Novosibirsk, 2003', 13, 10, '$' er DB 'Error!$' win DB 'This programme works in the MS-DOS(R) emulation only.$' ip8086 DB 'You have an Intel(R) 8086(TM) or Intel(R) 8088(TM), but the ' DB 'programme needs an', 13, 10, 'IA-32$' ip80286 DB 'You have an Intel(R) 80186(TM), Intel(R) 80188(TM) or ' DB 'Intel(R) 80286(TM), but', 13, 10, 'the programme needs an IA-32.$' cmos DB 'The battery of a CMOS-microscheme doesn', 39, 't work.$' AT_k DB 'An AT-keyboard is disconnected.$' ega DB 'This programme requires a colour EGA-compatible videoadapter.$' alr DB 'This programme has been already loaded.$' xms DB 'The XMS driver "Fuck_XMS" must have been loaded before.$' ent DB 13, 10, '$' END Start