Учебник по ассемблеру (ASM)
 

Ассемблер в примерах - Свободные кластеры логического диска "А"

Вопрос:
Разработать ассемблер-программу,выводящую в файл номера свободных кластеров логического диска "А". Для вывода использовать handle-ориентированные системные функции обработки файлов. Формат вывода - символьно-шестнадцатеричный. Пояснить назначение выводимых объектов и их отдельных полей. Для поиска объектов использовать только прерывания 13h.

Ответ:
Программа в приложении. Единственно, что еще следовало бы добавить - обработку ошибок типа "не читается диск".
	.model	tiny	;буддем создавать com-файл
	.186		;чтобы можно было было использовать команды shr ax,4

BOOT_STRUCTURE		struc			;структура данных бутовой записи
Jmp_Command		db	3 dup (?)	;jmp
Company_Name		db	8 dup (?)	;производитель
Sector_Size		dw	?		;размер сектора в байтах
Cluster_Size		db	?		;размер кластера в секторах
Reserved_Sectors	dw	?		;количество резервных секторов (после бутовой записи и до FAT-а)
FAT_Count		db	?		;количество FAT-ов
Root_Size		dw	?		;размер корневого каталога в 32-байтных записях (в секторе 16 штук)
Total_Sectors		dw	?		;общее число секторов
Media			db	?		;тип носителя
FAT_Size		dw	?		;размер одного FAT-а в секторах
Track_Sectors		dw	?		;количество секторов на дорожке
Heads_Count		dw	?		;количество головок
Hidden_Sectors		dw	?		;невидимые сектора (на флоппи их нет)
BOOT_STRUCTURE		ends

	.data
output_name	db	'free.dat',0		;имя файла
first_string	db	'Номера свободных кластеров диска A:',0dh,0ah ;заголовок в файле
len_first_string	equ	$-first_string 	;длина заголовка

	.data?
boot	db	512 dup (?)	;буфер для приема бутовой записи (и для вывода в файл)
fat	label	byte		;начиная с этого адреса запишем все сектора FAT-а

	.code
	.startup
	lea	bx, boot	;прочитаем бутовую запись
	mov	cx, 1		;0 дорожка, 1-й сектор
	mov	dx, 0		;0 головка, первый флоп
	mov	ax, 0201h	;читаем один сектор
	int	13h

	mov	si, bx		;будем адресовать бутовую запись через si
;число секторов одного FAT-а
	mov	cx, [BOOT_STRUCTURE ptr si].FAT_size
;адрес буфера для FAT-а
	lea	bx, fat
;определим первый сектор первого FAT-а 
;будем хранить в di номер считываемого сектора + дорожка (в формате регистра cx)
	mov	di, 2	;1-й - boot, ищем, начиная со 2-го
;а вдруг есть невидимые (на флоппи их обычно нет)
	add	di, [BOOT_STRUCTURE ptr si].Hidden_Sectors
	xor	dx, dx	;0 головка, первый флоп
sectors_loop:
	push	cx
	mov	cx,di	;зададим сектор+дорожку
	mov	ax,0201h;читаем один сектор
	int	13h

	inc	di	;инкремент номера сектора
;проверим, дошли ли до края дорожки?
	cmp	di, [BOOT_STRUCTURE ptr si].Track_Sectors
	jbe	next	;все ок, на следующий сектор
;переходим на другую головку
	mov	di, 1	;с первого сектора
	inc	dh	;увеличиваем номер головки
;проверим, прошли ли все головки?
	cmp	dh, byte ptr [BOOT_STRUCTURE ptr si].Heads_Count
	jb	next
;переходим на следующий цилиндр (дорожку)
	mov	dh, 0	;с 0-й дорожки
	add	di, 100h;инкремент цилиндра
next:
	add	bx, 200h;адрес буфера для следующего сектора
	pop	cx
	loop	sectors_loop	;по всем секторам

;Определим, сколько у нас есть кластеров
;Сначала посчитаем, сколько секторов использовано
	mov	di, 2	;начинаем со второго (первый - Boot)
	add	di, [BOOT_STRUCTURE ptr si].Hidden_Sectors ;невидимые
	xor	ax, ax
	mov	al, [BOOT_STRUCTURE ptr si].FAT_Count	;количество FAT-ов
	mul	[BOOT_STRUCTURE ptr si].FAT_size	;размер всех FAT-ов
	add	di, ax					;накапливаем

	mov	ax, [BOOT_STRUCTURE ptr si].Root_Size	;размер Root-а в записях
	shr	ax, 4					;всего в одном секторе 16 записей - делим на 16
	add	di, ax					;накапливаем

	mov	ax, [BOOT_STRUCTURE ptr si].Total_Sectors;общее число секторов
	sub	ax, di					;отнимем использованные

	mov	cl, [BOOT_STRUCTURE ptr si].Cluster_Size;число секторов в кластере
	dec	cl
	shr	ax, cl					;ax = числу кластеров

	dec	ax					;отнимем 1, т.к. первого нет, счет идет с 2

;просмотрим считанную FAT и найдем все свободные кластера
	mov	cx, ax					;счетчик кластеров

	mov	bp, bx					;где закончили писать сектора FAT-а - буфер для номеров
							; свободных кластеров
	xor	di, di					;индекс в буфере свободных кластеров
	mov	si, 2					;в si - номер очередного кластера

	lea	bx, fat+3				;пропустим первые 3 байта
search_free_loop:
	mov	ax, [bx]				;"четный" номер
	and	ax, 0fffh				;выделяем его
	jnz	next_odd				;не 0 - занят
	mov	[bp+di], si				;0 - свободен - сохраняем его номер
	add	di, 2					;для следующего
next_odd:
	inc	si					;номер следующего кластера
	loop	cmp_odd					;циклим по cx
	jmp	output_data				;если пройдем по всем, то на формирование файла
cmp_odd:
	mov	ax, [bx+1]				;"нечетный" номер
	shr	ax, 4					;выделяем его
	jnz	next_both				;не 0 - занят
	mov	[bp+di], si				;0 - свободен - сохраняем его номер
	add	di, 2
next_both:
	inc	si
	add	bx, 3					;на следующую пару
	loop	search_free_loop

output_data:						;сформируем файл с информацией
	lea	dx, output_name				;создадим файл
	mov	ah, 3ch
	mov	cx, 0
	int	21h

	mov	bx, ax					;handle файла

	lea	dx, first_string			;выведем заголовок
	mov	cx, len_first_string
	mov	ah, 40h
	int	21h

	mov	si, bp					;адрес буфера номеров
	mov	cx, di					;конечный использованный адрес
	shr	cx, 1					;число слов
new_row:
	xor	bp, bp					;будем выводить по 16 значений в строке, 
							; bp - счетчик в строке
	lea	di, boot				;адрес буфера для формирования очередной строки
row_loop:
	push	cx
	lodsw						;читаем очередное слово
	call	PrintWordHex				;сформируем в буфере hex-строку
	inc	bp					;считаем число величин в строке
	cmp	bp, 16					;16 - максимум
	jb	output_next				;меньше - добавим запятую и пробел
	mov	ax, 0a0dh				;иначе - добавим перевод строки
	stosw
	mov	cx, di					;адрес, куда дописали
	lea	dx, boot				;адрес буфера
	sub	cx, dx					;длина
	mov	ah, 40h					;и выведем 
	int	21h
	pop	cx
	loop	new_row					;на новую строку
	jmp	close_file				;попадем сюда, если будут ровно по 16
output_next:
	mov	ax, ' ,'				;разделим величины
	stosw	
	pop	cx
	loop	row_loop				;на следующее значение

	cmp	di, offset boot				;проверим, если есть незаконченная строка
	jne	close_file
	mov	word ptr [di-2],0a0dh			;закроем ее
	mov	cx, di					;и выведем
	lea	dx, boot	
	sub	cx, dx
	mov	ah, 40h
	int	21h
close_file:
	mov	ah, 3eh					;закроем файл
	int	21h

	.exit	0					;выход в ДОС

PrintWordHex	proc				;слово в hex
	push	ax				;сохраним al в стеке
	mov	al,ah				;выведем старший байт
	call	PrintByteHex
	pop	ax				;восстановим младший
PrintByteHex	proc				;выводим байт в hex
	push	ax				;сохраним младшую тетраду
	shr	al,4				;старшая тетрада 00-0f
	call	PrintHexDigit			;выводим hex цифру
	pop	ax				;восстановим младшую тетраду
	and	al,0fh				;выделим
PrintHexDigit	proc				;выводим hex цифру
	add	al,90h				;последующие 4 команды преобразовывают
	daa					;число 00-0f в символы '0'-'9','A'-'F'
	adc	al,040h
	daa
	stosb
	ret
PrintHexDigit	endp
PrintByteHex	endp
PrintWordHex	endp
	ret

	end


   Вперёд
   Содержание
Ophir nova 2 на сайте onsnab.ru.