Лабораторная работа № 3
(системное программирование и ОС)

Создание и использование библиотек объектных модулей.

Цель работы - изучить процесс создания и использования библиотек объектных модулей. Создать личную библиотеку объектных модулей.
(созданная Вами библиотека объектных модулей будет использоваться и пополнятся при выполнении следующих лабораторных работ).

(работа рассчитана на 4 - 6 час.)

Введение.

Технологически рациональнее часто используемые подпрограммы объединить в так называемый библиотечный файл или в библиотеку объектных модулей. Библиотеки объектных модулей могут содержать большое количество вспомогательных программных единиц. Очень часто подобные библиотеки объединяют в себе тематически однородные подпрограммы.

Кратко рассмотрим процесс получения (и загрузки для выполнения) загрузочного модуля.

1 шаг.  Создают файл  исходной программы  (SOURCE файл). Обычно для этого применяют  программу, называемую редактором текста (например EDIT имя_файла.ASM).

2 шаг. Транслируют исходную программу (файл), используя программу-ассемблер, и одновременно с получением распечатки программы на языке ассемблера (файл «листинг» - LST файл) помещают в файл полученную программу на машинном языке. Полученный таким образом файл представляет  собой файл объектной программы (OBJ файл). Например можно использовать следующую командную процедуру
c:\tasm\bin\tasm.exe /c /r /la /ml /os  /w2 /x /z /zi %1.asm,%1.obj,%1.lst

3 шаг.  Связывают (или преобразовывают внутренний формат объектного файла) файл объектной программы с библиотечными программами и другими программами с помощью редактора связей, и таким образом формируют файл загрузочного модуля ( EXE файл) в такой форме, при которой сразу возможна его загрузка в оперативную память и выполнение. Например можно использовать следующую командную процедуру
C
:\TASM\BIN\TLINK  /m /M /s /l /i /Tde  /v %1.obj,%1.exe,%1.map,MY_LIB.LIB;

4 шаг.  Выполняют загрузочный модуль. Вызывают системный загрузчик, который копирует содержимое исполняемого файла в оперативную память ЭВМ и настраивает центральный процессор на выполнение машинных команд, содержащихся в загрузочном модуле.

Схема работы редактора текста, ассемблера, редактора связей.

В приведенном выше примере вызова редактора связей файл MY_LIB.LIB - файл библиотеки объектных модулей.

Создание файлов объектных модулей.

Программа-ассемблер может быть последовательно применена к большому количеству исходных файлов, каждый из которых содержит одну программу или подпрограмму. В результате трансляции подпрограмм получаются ряд файлов объектных модулей.

Рассмотрим пример:

Разработаем три вспомогательные подпрограммы:

display -  для вывода символа из регистра dl,
kbin - для ввода с эхо сигналом одного символ в регистр al (при вводе ctrl-C - аварийное завершение ВСЕЙ программы),
crlf - для перевода курсора в следующую строку вывода
и поместим указанные подпрограммы соответственно в файлы
display.asm, kbin.asm и crlf.asm.

Обратите внимание на то, что в каждом из указанных файлов  имя подпрограммы и имя сегмента объявлено как  public. Это указывает ассемблеру (и редактору связей) на возможность использования этого объектного файла, как файла содержащего обще доступную подпрограмму и сегмент. Указание extrn display : far служит для сообщения компилятору об использовании "внешней" подпрограммы. В начале каждой из подпрограмм выполняется сохранение используемых регистров, а перед выходом из подпрограммы - восстановление. Оператор языка программирования ассемблера SEGMENT так же требует дополнительного "внимания". С целью размещения всех подпрограмм и "главной" программы в одном сегменте оперативной памяти следует этот оператор кодировать с указанием служебного слова  public. Подробнее об операторе SEGMENT можно посмотреть здесь.

Выполнив компиляцию этих подпрограмм - получим три файла объектных модулей, соответственно названных.

Создадим и откомпилируем программу использующую разработанные подпрограммы - ppp.asm.

Обратите внимание на начало тестовой программы:

CODE     SEGMENT 'CODE' PUBLIC
                EXTRN             display : far
                extrn                 kbin : far
                extrn                 crlf : far
                ASSUME CS:CODE

Команда языка программирования ассемблеа -   EXTRN служит для сообщения компилятору об использовании "внешней" подпрограммы. Вызов подпрограмм выполняется командами CALL.

            call crlf
            call crlf
            mov dl,'П'
            call display
            mov dl,'р'
            call display
Для получения загрузочного модуля программы ppp.asm необходимо с помощью редактора связей объединить все необходимые для работы программы ppp.asm объектные модули. Это можно сделать в командной строке при вызове редактора связей:

......>c:\TASM\BIN\TLINK /m /M /s /l /i /Tde /v ppp.obj+display.obj+crlf.obj+kbin.obj,ppp.exe,ppp.map;

или используя процедурный файл L1.bat

....>l1 ppp    +display.obj+crlf.obj+kbin.obj

Создадим свою библиотеку объектных модулей и поместим в нее разработанные подпрограммы.

Работа с библиотекой объектных модулей.

В системе программирования Tasm для работы с библиотечными файлами может быть использована программа  библиотекарь - TLIB.

Программа библиотекарь создает новую библиотеку и/или добавляет/заменяет  в ней требуемый объектный модуль.
Типичное обращение к библиотекарю можно осуществить используя следующий процедурный файл lib.bat:

ECHO ON
c:\TASM\BIN\TLIb mylib.lib -+%1.obj, mylib.lst
 

В этом примере - - имя библиотечного файла, mylib.lst - файл протокола работы библиотекаря,
-+%1.obj - имя добавляемого/заменяемого объектного модуля.

Для создания библиотеки mylib.lib и помещения туда созданных ранее объектных модулей необходимо выполнить:

lib display
lib crlf
lib kbin

Для сборки программы ррр с использованием библиотечного файла достаточно выполнить

......>c:\TASM\BIN\TLINK /m /M /s /l /i /Tde /v ppp.obj,ppp.exe,ppp.map,mylib.lib;

Или использовать процедурный файл LL.bat:

ECHO ON
c:\TASM\BIN\TLINK /m /M /s /l /i /Tde /v %1.obj%2,%1.exe,%1.map,mylib.lib;
 

выполнив
...
>ll ppp

получим рабочую (загружаемую) программу.

Рассмотрим следующий пример.

Программа преобразования  чисел в шестнадцатеричные символы

Для работы с табличными данными в системе команд предусмотрена команда xlatb (а также xlatw).

Если в регистре AL записано число n (0 <= n <=  255), то по команде xlatb оно замещается содержимым n-й позиции из специально подготовленной таблицы соответствий.

Если обозначить полученные в результате значения через а(n), то xlatb можно назвать командой преобразования п в а(п).

Эта команда удобна для преобразования кодов.

В качестве примера можно привести таблицу десятичных чисел n от 0 до 15 и соответствующих им шестнадцатеричных чисел а(n).

n 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
a(n) '0' '1' '2' '3' '4' '5' '6' '7' '8' '9' 'A' 'B' 'C' 'D' 'E' 'F'

 

Ниже показана программа преобразования чисел n, представленных в ЭВМ в двоичной системе счисления, в n-е (по порядку) значения символов (чисел) от 0 до F.

code    segment 'CODE' PUBLIC
        assume cs:code
        extrn display : far
main     proc far
        push ds
        mov ax,0
        push ax

        mov ax,ddd
        mov ds,ax
        assume ds:ddd

        mov si,0      ; начальное двоичное значение
        mov cx,16     ; сколько раз увеличить и вывести
        lea bx,table  ; установка таблицы перекодировки
next:   mov ax,si     ; значение двоичного кода для перекодировки
        xlatb         ; собственно перекодировка.... в al
        mov dl,al     ; что выводим на консоль
        call display
        inc si
        loop next
        ret
main    endp
code    ends
ddd     segment
table   db '0123456789ABCDEF'
ddd     ends

        end main

(текст программы можно посмотреть здесь)

Рис. Программа преобразования двоичных чисел в шестнадцатеричные символы по команде xlatb.

Собственно преобразование осуществляется командой xlatb (translate byte), а вся программа работает по следующему алгоритму.

1. Провести подготовку к передаче управления MS-DOS.

2. Поместить содержимое DS  (сегмента данных) в CS (сегмент кодов), так как область table находится в самой программе  (в сегменте кодов).

3. Поместить начальное значение n, равное 0, в регистр SI, значение 16 в регистр СХ, а адрес первой позиции таблицы в регистр ВХ. Для выполнения команды xlatb необходимо, чтобы адрес начала таблицы обязательно находился в регистре ВХ. Это осуществляется по команде lea (load effective address). Вместо этой команды можно использовать команду

mov bx, offset table

Следует обратить внимание на то, что если вместо этой команды использовать команду mov bx, table, то в регистр ВХ будет введен не адрес table, а первые два байта из области table.

4. Далее переслать содержимое (n) регистра SI в регистр АХ (на самом деле для этого используется только правая половина (младшие байты) регистра, т.е. регистр AL), и по команде xlatb преобразовать n в а(n). Поскольку преобразованное значение формируется в регистре AL, переслать содержимое регистра AL в регистр DL и вывести на экран дисплея по команде call display.

5. По команде inc увеличить значение n на 1 в регистре SI. Затем по команде loop уменьшить значение регистра СХ на 1, и, если СХ <>  0, возвратиться по адресу next. В случае СХ = 0 закончить выполнение программы.

Команде xlatb аналогична команда xlatw (translate word). По этой команде производится преобразование данных длиной не 1 байт, а 2 байт. Действие этой команды ничем не отличается от действия команды xlatb, за исключением того, что вместо регистра AL используется регистр АХ. Необходимо обратить внимание на то, что в рассматриваемой программе значения n находятся в диапазоне 0 <=  n <=  15. В противном случае (при n < 0 и n > 15) преобразование будет выполнено для данных, находящихся вне области table, и полученные в результате значения а(n) непредсказуемы.

Используя описанную выше программу легко составить библиотечную подпрограмму преобразования. Подобные подпрограммы очень удобны при организации вывода значений из оперативной памяти и регистров.

Ниже представлена программа, выполняющая преобразование в двухразрядные шестнадцатеричные символы (числа).

Данными, которые подвергаются преобразованию, являются 16 десятичных чисел: 15, 16, 17, .... 30, — последовательно формируемые в регистре SI (в программе main).

В подпрограмме преобразования hex после формирования содержимого регистра DS данные, находящиеся в регистре DX, помещаются в стек, и затем в регистр ВХ загружается значение начального адреса table; младшие 8 бит содержимого регистра DL пересылаются в регистр AL.

Затем, используя четыре команды shr, перемещают 4 старших бита в регистре AL на место 4 младших бит, по команде xlatb преобразуют их в шестнадцатеричное число и выводят на экран дисплея.

Затем по команде pop ax возвращают содержимое регистра АХ, ранее занесенное в стек по команде push dx, и выделяются младшие биты регистра AL по команде and с помощью маски — двоичного числа 00001111b.

Далее вновь выполняется преобразование в шестнадцатеричные символы (число).

Отметим, что в программе были использованы команды push/pop, в то время как указания адреса сегмента стека в ней не содержится. Это объясняется тем, что в данном случае используется стек, предусмотренный в ОС.

 

        title  d-hex.asm
code    segment 'CODE' PUBLIC
        assume cs:code
        extrn display : far

main     proc far
        push ds
        mov ax,0
        push ax

        mov ax,ddd
        mov ds,ax
        assume ds:ddd

        mov si,0 ; начальное двоичное значение
        mov cx,16 ; сколько раз увеличить и вывести
        lea bx,table ; установка таблицы перекодировки
next:   mov dx,si ; значение двоичного кода для перекодировки
        Call hex ; собственно перекодировка и печать

        mov dl,' ' ; печать разделителя
        call display
        inc si
        loop next
        ret
main    endp
;---------------
hex     proc
        push dx
        lea bx,table
        mov al,dl ; первый полу байт
        clc
        shr al,1
        shr al,1
        shr al,1
        shr al,1
        xlatb
        mov dl,al
        call display
        pop ax ; второй полу байт
        and al,00001111b
        xlatb
        mov dl,al
        call display
        ret
hex     endp
;============
code   ends
ddd    segment
table  db '0123456789ABCDEF'
ddd    ends

        end main

(текст программы можно посмотреть здесь)

Рис. Программа преобразования десятичных чисел в двухразрядные шестнадцатеричные символы (числа).

Если использовать подпрограмму hex, показанную выше, легко можно написать
программу преобразования  двоичных чисел в четырехразрядные шестнадцатеричные символы (числа).

Такой пример приведен ниже. Алгоритм работы этой программы следующий.

1.       Вывести на экран дисплея символ ?.

2.       Ввести с клавиатуры в регистр AL один символ, используя подпрограмму kbin.

3.       Содержимое регистра AL также переслать в регистр АН. При этом в регистре АХ окажутся размещенными продублированные шестнадцатиразрядные данные.

4.       Переслать содержимое регистра АХ в регистр SI, после чего отобразить на экране дисплея символ =.

5.       С помощью подпрограммы hexw представить содержимое регистра DX, восстановленного из регистра SI, в вида четырехразрядного шестнадцатеричного числа.

В соответствии с подпрограммой hexw, которая используется на 5-м шаге этой программы, вначале содержимое регистра DH пересылается в регистр DL и на экран дисплея выводятся два старших разряда искомого числа, затем половина содержимого регистра DX, восстановленного из регистра DI (содержимое регистра DL), выводится на экран дисплея в качестве двух младших разрядов образовавшегося числа. Подпрограмма crlf используется для перемещения курсора в начало следующей строки. Если посмотреть на результат выполнения программы, то легко видеть, что код ASCII для введенных символов в шестнадцатеричной системе счисления представляется двумя одинаковыми парами символов.

 

       title d-hexw.asm
code  segment  'CODE' public
       assume cs:code
       extrn display : far
       extrn kbin    : far
       extrn hex     : far
       extrn crlf    : far 
main   proc  far
       push  ds
       mov   ax,0
       push  ax
 
      mov  dl,'?'
      call display
      call kbin
      mov  ah,al
      mov  si,ax
      mov  dl,'='
      call display
      mov  dx,si
      call hexw
      call crlf
      jmp  main
main  endp
;--------------------
hexw  proc
      mov  di,dx
      mov  dl,dh
      call hex
      mov  dx,di
      call hex
      mov  dl,' '
      call display
      ret
hexw  endp
;============
code ends
     end main

(текст программы можно посмотреть здесь)

Рис. Программа преобразования двоичных чисел в четырехразрядные шестнадцатеричные символы (числа).

Более формальную информацию о подпрограммах (процедурах) можно посмотреть здесь.

 

Задание на лабораторную работу

  1. Создайте в своей папке исходные модули подпрограмм display, crlfkbin.

  2. Создайте процедуры для работы с личной библиотекой объектных модулей.

  3. Создайте свою библиотеку объектных модулей и поместите туда подпрограммы display, crlfkbin.

  4. Разработайте подпрограммы hex и hexw на основе приведенных выше примеров.

  5. Выполните тестирование разработанных подпрограмм и поместите их в свою библиотеку объектных модулей.

  6. Напишите тестовую программу, использующую разработанные подпрограммы, выполните ее отладку и тестирование.

  7. Рассмотрите тестовую программу pp1.asm, разберитесь с ее назначением и выполните ее.

  8. Разработайте на основе программы pp1.asm подпрограмму kbin2 для ввода по запросу двух символов с клавиатуры и помещения их в регистр AX.

  9. Выполните отладку и поместите разработанную подпрограмму в свою библиотеку.

  10. Перепишите программу  pp1.asm  с использованием подпрограммы kbin2.

  11. Все листинги подпрограмм, программ и результатов тестирование поместите в отчет.

Контрольные вопросы

- что такое библиотека объектных модулей?

- как создать библиотеку объектных модулей?

- как использовать библиотеку объектных модулей?

- что содержит библиотека объектных модулей?

- зачем используются команды ассемблера - public  и extern?

- использование команды xlatb

- вопросы по программам display, crlfkbin  hex  hexw  pp1 - основной алгоритм и команды.