Дата: 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"-файл. Вызываться он может так:
- из командной строки ОС MS-DOS®;
- из пакетного файла (".bat"-файл);
- из файла автоконфигурации ОС MS-DOS® "Config.sys" с помощью команды "Install" или "HighInstall".
Вообще для нормальной работы драйвера при его запуске требуется ЦП в 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 основных факторов:
- от частоты работы микропроцессора;
- от реального объёма памяти ОЗУ, присутствующей в ЭВМ.
Программа написана на языке программирования 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 программа перехватывает нажатие комбинаций " +
; + " и " + + ". Первая комбинация сохраняет образ памяти,
; вторая - его восстанавливает.
; Аналогичные действия клавиатурным комбинациям могут выполнять приложения,
; используя вызов прерывания 18h. Предварительно следует записать параметры в
; регистр AX:
; 1) AX = FE00h (при восстановлении образа памяти ОЗУ из XMS-памяти);
; 2) AX = FE01h (при сохранении образа памяти ОЗУ в XMS-памяти);
; 3) int 18h.
; При AX = FE00h вызов прерывания 18h подобен нажатию комбинации " +
; + "; при AX = FE01h, - соответственно, комбинации " +
; + ".
; Перед оставлением резидентной части программа предварительно анализирует
; присутствие своей предыдущей копии в памяти ОЗУ. Если копия обнаружена,
; программа не остаётся повторно резидентной. Так можно определить, загружен ли
; данный резидент в память ОЗУ или ещё нет. Всякое приложение также может
; определить её присутствие, воспользовавшись прерыванием 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 " + + " to save an '
DB 'image into the XMS, " +', 13, 10, ' + " 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