MBR
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 10.11.03 12:02
Оценка: 52 (4)
Раз зашел разговор про MBR, выложу свои мытарства. Загрузчик для дискеты, заточеный на то, чтобы передать управление программе на Turbo Pascal. Единственное, что мне непонятно, так это то, что необходимо настроить значения вектора 1E, но... раз все так поступают, то и я не был оригинальным.

Когда-то даже документацию по этому делу решил написать, но не сильно преуспел.

;*********************************************************
; Загрузчик для дискеты 
; Версия 1.10
; (С) Mystic 22.11.2002
;*********************************************************
;
; Описание:
;   Выполняет загрузку EXE-файла, который должен распола-
;   гаться в корневой директории (но не обязательно пер-
;   вым). PSP не поддерживается.
;
; Символы препроцессора:
;   DOS:     предполагает, что значение CS <> 0. Если этот
;           символ задан, то возможна отлака файла из DOS.
;
; Константы:  
;   ExeName: имя EXE-файла в формате 8.3 так, как оно 
;           прописано в FAT.
;   SkipIP:  задает число, которое будет добавлено к точке 
;           входа. При помощи этого параметра можно про-
;           пустить код загрузки. Для программ, скомпили-
;           рованных в TP 7.0 необходимо установить этот 
;           параметр в 15.
; 
; История:
;   версия 1.10
;     [*] оптимизирован код
;     [+] добавлен символ препроцессора DOS
;     [+] добавлен вывод сообщений об ошибках
;     [+] установка собственного вектора 1E
;     [+] расширены комментарии  
;*********************************************************






IDEAL
P8086          
MODEL TINY     

CODESEG

STARTUPCODE

IFDEF DOS
  ; Для отладки из-по DOS
  ORG 100h
  JMP NEAR START
ENDIF

ORG 7C00h
START: 







;======================================================
; Параметры FAT
;======================================================

JMP NEAR EntryPoint
OemName                 DB    '  MWOS  '
BytePerSector           DW          512d
SectorPerCluster        DB            1d
ReservedSector          DW            1d
FatCount                DB            2d
RootEntries             DW          224d
SectorCount             DW         2880d 
MediaDescriptor         DB          0F0h
SectorPerFat            DW            9d
SectorPerTrack          DW           18d
HeadPerTrack            DW            2d
HiddenSector            DD             0
BigTotalSector          DD             0
Drive                   DB             0
                        DB             0
ExtendedBootSignature   DB           ')'   
SerialNumber            DD    0FACE0000h           
VolumeLabel             DB '           '
FileSystemID            DB    'FAT12   '     








;======================================================
; Константы и переменные
;======================================================

ExeName       DB  'LOADER  EXE'    ; Файл, который будем загружать
SkipIP        DW 15d               ; Загрузчик
Int1E         DW 0FFF0h, 0         ; Указатель стека/сохраненный вектор 1Eh







;======================================================
; Сообщения об ошибках...
;======================================================

IOErrorMsg DB 'Disk I/O error', 10, 13
IOErrorLen DW $ - IOErrorMsg

FileNotFoundMsg DB 'File not found', 10, 13
FileNotFoundLen DW $ - FileNotFoundMsg







;======================================================
; Чтение ОДНОГО сектора
;  AX -> номер сектора (от 0 до SectorCount-1)
;  ES:BX -> буфер, в который записываются прочитанные данные
;  AX, CX, DX <- ?
;======================================================

PROC ReadSector NEAR
  CWD
  DIV [SectorPerTrack] 
  MOV CL, DL
  INC CL
  CWD
  DIV [HeadPerTrack]
  MOV CH, AL
  MOV DH, DL
  MOV DL, [Drive]
  MOV AX, 0201h
  INT 13h
  JC  IOError
  RET
ENDP ReadSector







;======================================================
; Чтение ОДНОГО кластера
;  AX -> номер кластера
;  ES:BX -> буфер, в который записываются прочитанные данные
;  ES:BX <- указатель на конец буфера для чтения
;  CX <- 0
;  AX, DX <- ?
;======================================================

PROC ReadCluster NEAR
  DEC AX
  DEC AX
  MOV CL, [SectorPerCluster]
  XOR CH, CH
  MUL CX
  ADD AX, BP
  ; JMP ReadSectors --- это следующий адрес :)
ENDP ReadCluster






;======================================================
; Чтение произвольного количества секторов
;  AX -> номер сектора (от 0 до SectorCount-1)
;  CX -> количество читаемых секторов
;  ES:BX -> буфер, в который записываются прочитанные данные
;  ES:BX <- указатель на конец буфера для чтения
;  CX <- 0
;  AX, DX <- ?
;======================================================

PROC ReadSectors NEAR
  ; JCXZ @@2  Assert(CX<>0)
@@1:
  PUSH CX
  PUSH AX
  CALL ReadSector
  POP AX
  POP CX
  ADD BX, [BytePerSector]
  INC AX
  LOOP @@1
@@2:
  RET
ENDP ReadSectors







;======================================================
; Завершение загрузки в результате ошибки
; Выводит сообщение, ждет нажатия клавиши и 
;  завершает загрузку
; ES:BP -> Выводимое на экран сообщение
; CX -> Количество выводимых символов
;======================================================

PROC Abort NEAR
  
  MOV AX, 03h
  XOR BH, BH
  INT 10h

  MOV AX, 1301h
  MOV BL, 07h
  INT 10h

  XOR AH, AH
  INT 16h

  IFDEF DOS           ;  Данный фрагмент заносит 
    XOR AX, AX        ; в ES значение 0. Под DOS
    MOV ES, AX        ; мы заносим значение ES явно 
  ELSE                ; а при загрузке с дискеты мы 
    PUSH CS           ; экономим пару байт, учитывая,
    POP  ES           ; что CS = 0.
  ENDIF

  MOV DI, 1Eh * 4
  MOV SI, OFFSET Int1E
  MOVSW
  MOVSW
  IFDEF DOS
    MOV AX, 4C00h
    INT 21h
  ELSE
    INT 19h
  ENDIF
ENDP Abort





;======================================================
; Завершение загрузки в результате ошибки ввода/вывода
;======================================================

PROC IOError NEAR
  MOV CX, [IOErrorLen]
  MOV BP, OFFSET IOErrorMsg
  JMP Abort
ENDP IOError





;======================================================
; Завершение загрузки (не найден загружаемый файл)
;======================================================

PROC FileNotFound NEAR
  MOV CX, [FileNotFoundLen]
  MOV BP, OFFSET FileNotFoundMsg
  JMP Abort
ENDP FileNotFound  






PROC EntryPoint NEAR




  ; ===========================================================
  ; Инициализация:
  ;   Установка стека
  ;   Сохранение старого значения Int 1E
  ;   Установка параметров дискеты (Int 1E)
  ;   Установка сегментных регистров
  ; ===========================================================
  CLD
  LES   DI, [DWORD Int1E]
  MOV   SP, DI
  LDS   SI, [ES:1Eh * 4]
  MOV   [ES:1Eh * 4], DI
  MOV   [ES:1Eh * 4 + 2], CS
      IFDEF DOS    ; Для отладки COM файла!!!
        PUSH CS    ; Для случая DOS необязятельно CS=0
        POP  ES    ; Поэтому надо переустановить ES
      ENDIF        ; Для корректроно обращения к Int1E
  MOV   [ES:Int1E], SI
  MOV   [ES:Int1E+2], DS
  MOV   CX, 11
  REP   MOVSB 
  MOV   [BYTE ES:DI-2], 15
  PUSH CS
  POP DS          
  ; На текущий момент:
  ; ES=DS=CS [=0]
  ; SP = 7BE0
  ; CX = 0
  ; AX, BX, DX, SI, DI, BP = ?
  ; ===========================================================






  ; ===========================================================
  ; Чтение FAT
  ; ===========================================================
  MOV   BX, OFFSET FAT_TABLE
  MOV   AX, [ReservedSector]
  MOV   CX, [SectorPerFat]
  CALL  ReadSectors
  ; На текущий момент:
  ; ES=DS=CS [=0]
  ; ES:BX = Конец прочитанной первой копии FAT
  ; CX = 0
  ; AX, DX, SI, DI, BP = ?
  ; ===========================================================




  ; ===========================================================
  ; Чтение корневого каталога
  ; ===========================================================
  MOV AX, [RootEntries]
  MOV CL, 5
  SHL AX, CL
  CWD
  MOV CX, [BytePerSector]
  DIV CX
  MOV CX, AX   ; CX --- количество секторов, занимаемых корневым каталогом
  MOV AL, [BYTE SectorPerFat]
  MUL [BYTE FatCount]
  ADD AX, [ReservedSector] ; AX --- начальный сектор корневого каталога
  MOV BP, AX
  ADD BP, CX       ; BP --- первый сектор второго! кластера
  PUSH BX
  CALL ReadSectors
  POP  DI
  ; На текущий момент:
  ; ES=DS=CS [=0]
  ; ES:DI = Начало корневого каталога
  ; ES:BX = Конец корневого каталога
  ; BP = первой сектор второго кластера
  ; CX = 0
  ; BP
  ; AX, DX, SI = ?
  ; ===========================================================






  ; ===========================================================
  ; Ищем наш файлик...
  ; ===========================================================
@@1:
  CMP DI, BX
  JGE  FileNotFound
  MOV  SI, OFFSET ExeName
  MOV  CL, 11
  REPE CMPSB
  JZ @@Found
  ADD  DI, CX
  ADD DI, 21
  JMP @@1
  ; На текущий момент:
  ; ES=DS=CS [=0]
  ; ES:DI = Конец имени файла в элементе корневого каталога
  ; ES:BX = Конец корневого каталога
  ; DS:SI = SkipIP (поскольку идет сразу за ExeName)
  ; BP = первой сектор второго кластера
  ; CX = 0
  ; AX, DX = ?
  ; ===========================================================






  ; ===========================================================
  ; Выделяем память под найденный файл...
  ; ===========================================================
@@Found:
  MOV AX, [WORD DS:DI+15]
  PUSH AX
  PUSH BX
  CALL ReadCluster
  POP BX
      IFDEF DOS                    ; Для случая DOS пользуемся тем, 
        MOV ES, CX                 ; что после вызова ReadCluster,
        MOV AX, [WORD ES:0413h]    ; значение CX = 0, настраиваем ES и
      ELSE                         ; обращаемся к области данных BIOS. 
        MOV AX, [WORD DS:0413h]    ; При загрузке CS=DS=0, поэтому 
      ENDIF                        ; настройка не требуется.
  MOV CL, 6
  SHL AX, CL
  MOV DX, [WORD DS:BX+0004h]  ; Размер EXE в 512-байтовых страницах
  DEC CL
  SHL DX, CL
  SUB AX, DX
  SUB AX, [WORD DS:BX+000Ah]  ; Минимальный HEAP
  MOV ES, AX
  XOR BX, BX
  POP AX
      IFDEF DOS    ; Для случая DOS писать куда нам хочеться
        PUSH CS    ; нельзя, поэтому переустанавличаем ES
        POP  ES    ; на начало сегмента. Результат предыдущих
      ENDIF        ; нужен только для проверки правильности кода. 
  PUSH ES
  ; На текущий момент:
  ; DS=CS [=0]
  ; ES:BX = Место, куда будет скопирован EXE-файл
  ; DS:DI = Конец имени файла в элементе корневого каталога
  ; DS:SI = SkipIP (поскольку идет сразу за ExeName)
  ; AX = Первый кластер EXE-файла
  ; CX = 5
  ; DX = Чему-то из заголовка EXE-файла
  ; BP = первой сектор второго кластера
  ; Stack[0] = Сегмент, начиная с которого читается EXE-файл
  ; ===========================================================





  ; ===========================================================
  ; Чтение EXE-файла
  ; ===========================================================
@@ReadLoop:
; Читаем кластер
  PUSH AX
  CALL ReadCluster

; Коррекция на переполнение
  MOV AX, ES
  MOV CL, 4
  SHR BX, CL
  ADD AX, BX
  MOV ES, AX
  XOR BX, BX
  POP AX

; Находим следующий кластер
  MOV DI, AX
  SHR DI, 1
  PUSHF
  ADD DI, AX
  ADD DI, OFFSET FAT_TABLE
  MOV AX, [WORD DS:DI]
  POPF
  JC @@OddCluster
  AND AH, 0Fh
  JMP @@FindComplete
@@OddCluster:
  MOV CL, 4
  SHR AX, CL
@@FindComplete:
  
  CMP AX, 0FF8h
  JL  @@ReadLoop
  POP DX
  MOV DS, DX
  ; На текущий момент:
  ; CS [=0]
  ; ES:BX = Конец прочитанного EXE-файла
  ; CS:SI = SkipIP (поскольку идет сразу за ExeName)
  ; DS, DX =  Сегмент, начало заголовка EXE-файл
  ; AX, CX, DI = ?
  ; BP = первой сектор второго кластера
  ; ===========================================================








  ; ===========================================================
  ; Настройка адресов
  ; ===========================================================
  ADD DX, [WORD DS:0008h]   ; Размер заголовка в 16-байтовых параграфах
  MOV SI, [WORD DS:0018h]   ; Первый элемент таблицы перемещения
  MOV CX, [WORD DS:0006h]   ; Число элементов аблицы перемещения
  JCXZ @@RelocationComplete
@@RelocationNext:
  LODSW
  MOV BP, AX
  LODSW
  ADD AX, DX
  MOV ES, AX
  MOV AX, [WORD ES:BP]
  ADD AX, DX
  MOV [WORD ES:BP], AX
  LOOP @@RelocationNext
  ; На текущий момент:
  ; CS [=0]
  ; DS = Сегмент, начало заголовка EXE-файла
  ; DX = Сегмент, начало образа EXE-файла
  ; CX = 0
  ; AX, BX, SI, DI, BP, ES = ?
  ; ===========================================================









  ; ===========================================================
  ; GO!
  ; ===========================================================
@@RelocationComplete:
  MOV SP, [WORD DS:0010h]   ; Значение SP
  MOV AX, [WORD DS:000Eh]   ; Значение SS
  ADD AX, DX
  MOV SS, AX
  MOV AX, [WORD DS:0016h]   ; Значение CS
  ADD AX, DX
  PUSH AX
  MOV AX, [WORD DS:0014h]
  ADD AX, [WORD CS:SkipIP]
  PUSH AX
  RETF 
  ; На текущий момент:
  ; CS:IP Точка входа в EXE-файл + SkipIP
  ; SS:SP Стек EXE-файла
  ; DS = Сегмент, начало заголовка EXE-файла
  ; DX = Сегмент, начало образа EXE-файла
  ; CX = 0
  ; AX, BX, SI, DI, BP, ES = ?
  ; ===========================================================

ENDP

ORG 7DFEh
  DB 055h, 0AAh
  FAT_TABLE: 

END
... << RSDN@Home 1.1 beta 2 >>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.