Консультация № 195817
06.06.2019, 16:14
0.00 руб.
0 20 1
Здравствуйте! Прошу помощи с реализацией задачи.
Изменить дату и время создания файла MYFILE.txt и вывести их старые и новые значения на экран.
1. Формат программы: COM - программа
2. Ввод с клавиатуры: средствами DOS
3. Вывод на экран: в текстовом режиме
4. Динамическое распределение памяти: определение размера программы.
Надеюсь на Вашу помочь! Заранее благодарен!

Обсуждение

давно
Посетитель
7438
7205
06.06.2019, 18:53
общий
Адресаты:
Помочь, в смысле сделать за Вас?
Чего Вы достигли в написании программы?
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
403112
9
06.06.2019, 23:55
общий
Наработки есть. Другое дело, что не получается поменять дату и время создания файла на желаемое. Второй момент: вывод старых и новых значений на экран. Не хватает понимания в реализации.

Код:
TITLE PROG
.MODEL SMALL
.data
filename DB 'C:\LOL\MYFILE.txt',0
handle dw ?
sec dw 8/2
min dw 25*32
hour dw 19*2048
day dw 16
mon dw 8*32
year dw 15*512
Error db 'Error!',13,10,'$'
.CODE
ORG 100H
BEGIN:
mov ah,3dh
mov al,0
lea dx, filename
int 21h
MOV handle,ax
jc MError

mov ah,57h
mov al,1
mov bx,handle
mov cx,0
OR CX,sec
OR CX,min
OR CX,hour
mov DX,0
OR DX,day
OR DX,mon
OR DX,year
INT 21H

mov ah,3eh
int 21h
jnc NoError

MError:
lea dx, Error
mov ah,09h
int 21h

NoError:
mov ax,4c00h
int 21h
END BEGIN
давно
Посетитель
7438
7205
07.06.2019, 19:45
общий
это ответ

Здравствуйте, MarkKnopfler!
Держите программу... Можно по-разному сделать, я сделал так...
Комментариев не писал, разбирайтесь самостоятельно... Будут вопросы, спрашивайте в мини-форуме.
Не забывайте, программа в формате COM (Ваш пример - EXE)
Для преобразования байта - двоичного числа в две цифры использована команда AAM
При вводе даты/времени ожидается ввод только в ожидаемом виде и проводится проверка на корректность.
Единственная неточность: день проверяется всегда на 31. Надо проверять на максимальный для каждого месяца
и с учетом високосного года. Этим я пренебрег для простоты :)
И наконец, для чего пункт 4. Динамическое распределение памяти?
[code lang=asm h=200] TITLE PROG
.MODEL tiny
.data
filename DB 'MYFILE.txt',0
Error db 13,10,'Error!',13,10,'$'
MessageOld db 'Old $'
MessageNew db 0dh,0ah,'New $'
MessageDateTime db 'date: '
day_pos db 'xx.'
month_pos db 'xx.'
year_pos db 'xxxx, time:'
hours_pos db 'xx:'
minutes_pos db 'xx:'
seconds_pos db 'xx',0dh,0ah,'$'

MessageEnterDate db 'Enter date (dd.mm.yyyy): $'
MessageEnterTime db 0dh,0ah,'Enter time (hh.mm.ss): $'

InBuffer db 128
InCount db ?
InData db 128 dup (?)

.CODE
.186
ORG 100H
BEGIN:
mov ah, 3dh
mov al, 0
lea dx, filename
int 21h
jc MError

mov bx, ax

mov ax, 5700h ;read time/date of file
int 21h

lea si, MessageOld
call PrintTimeDate

call EnterNewTimeDate
jc MError

mov ax, 5701h
int 21h

lea si, MessageNew
call PrintTimeDate

mov ah, 3eh
int 21h
jnc NoError
MError:
lea dx, Error
mov ah,09h
int 21h
NoError:
mov ax,4c00h
int 21h

PrintTimeDate proc near
push dx
mov dx, si
mov ah, 9
int 21h
pop dx
push dx
push cx
mov ax, dx
and ax, 001fh
aam
or ax, '00'
mov day_pos, ah
mov day_pos+1, al

mov ax, dx
and ax, 01e0h
shr ax, 5
aam
or ax, '00'
mov month_pos, ah
mov month_pos+1, al

mov ax, dx
shr ax, 9
add ax, 1980
mov cl, 100
div cl
push ax
aam
or ax, '00'
mov year_pos, ah
mov year_pos+1, al
pop ax
mov al, ah
aam
or ax, '00'
mov year_pos+2, ah
mov year_pos+3, al

pop cx
mov ax, cx
shr ax, 11
aam
or ax, '00'
mov hours_pos, ah
mov hours_pos+1, al

mov ax, cx
and ax, 07e0h
shr ax, 5
aam
or ax, '00'
mov minutes_pos, ah
mov minutes_pos+1, al

mov al, cl
and al, 1fh
shl al, 1
aam
or ax, '00'
mov seconds_pos, ah
mov seconds_pos+1, al

lea dx, MessageDateTime
mov ah, 9
int 21h
pop dx
ret
PrintTimeDate endp

EnterNewTimeDate proc near
lea dx, MessageEnterDate
mov ah, 9
int 21h
lea dx, InBuffer
mov ah, 0ah
int 21h
cmp byte ptr InCount, 10
jne EnterNewTimeDate_error

lea si, InData
call GetNum2
jc EnterNewTimeDate_error
cmp ax, 31
jg EnterNewTimeDate_error
test ax, ax
jz EnterNewTimeDate_error
mov di, ax

lea si, InData+3
call GetNum2
jc EnterNewTimeDate_error
cmp ax, 12
jg EnterNewTimeDate_error
test ax, ax
je EnterNewTimeDate_error
shl ax, 5
or di, ax

lea si, InData+6
call GetNum4
jc EnterNewTimeDate_error
sub ax, 1980
jl EnterNewTimeDate_error
cmp ax, 119
jg EnterNewTimeDate_error
shl ax, 9
or di, ax

lea dx, MessageEnterTime
mov ah, 9
int 21h
lea dx, InBuffer
mov ah, 0ah
int 21h

cmp byte ptr InCount, 8
jne EnterNewTimeDate_error

lea si, InData
call GetNum2
jc EnterNewTimeDate_error
cmp ax, 23
jg EnterNewTimeDate_error
shl ax, 11
mov cx, ax

lea si, InData+3
call GetNum2
jc EnterNewTimeDate_error
cmp ax, 59
jg EnterNewTimeDate_error
shl ax, 5
or cx, ax

lea si, InData+6
call GetNum2
jc EnterNewTimeDate_error
cmp ax, 59
jg EnterNewTimeDate_error
shr ax, 1
or cx, ax

mov dx, di
clc
ret
EnterNewTimeDate_error:
stc
ret
EnterNewTimeDate endp

GetNum4 proc
call GetNum2
jc GetNum4_ret
mov bp, 100
mul bp
push ax
call GetNum2
pop bp
jc GetNum4_ret
add ax, bp
clc
GetNum4_ret:
ret
GetNum4 endp

GetNum2 proc
lodsb
cmp al, '0'
jl GetNum2_error
cmp al, '9'
jg GetNum2_error
and al, 0fh
mov ah, 10
mul ah
mov dx, ax
lodsb
cmp al, '0'
jl GetNum2_error
cmp al, '9'
jg GetNum2_error
and ax, 0fh
add ax, dx
clc
ret
GetNum2_error:
stc
ret
GetNum2 endp

END BEGIN
[/code]
5
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
403112
9
09.06.2019, 18:31
общий
09.06.2019, 18:33
Добрый день!

Прошу прощения за относительно нескорый ответ. Пытался разобраться в Вашей программе: что-то получилось понять, а что-то нет. В первую очередь хотел бы выразить огромную благодарность за Ваш труд. Восхищён Вашими умениями!

Что касается самой программы, то хотелось бы услышать объяснение по нескольким моментам, будьте добры:
1. Каким образом происходит преобразование значения из регистра CX в привычный формат времени? Можно на каком-нибудь примере показать? С датой преобразования понятны, а вот со временем...
2. Для чего используется в программе InData (и в частности инструкции: lea si, InData / lea si, InData+3 / lea si, InData+6)?
3. Что происходит в процедурах GetNum2 и GetNum4? Интуитивно понял, что они позволяют записать 2 цифры и 4 (для года) соответственно, а вот детали — голова кругом

И отвечая на Ваш вопрос о 4-м пункте — для подсчёта размера программы. Т.е. программа должна выводить его. В каких справочниках / учебниках / источниках об этом можно прочитать?

И последний вопрос: как можно финансово поддержать ваш проект? Не нашёл соответствующего раздела
давно
Посетитель
7438
7205
10.06.2019, 12:08
общий
11.06.2019, 14:32
Адресаты:
Есть вопросы - это замечательно Итак:
1) Формат времени файла в функциях ДОСа следующий (побитно):
CX = hhhhhmmm mmmsssss, где
hhhhh - часы, старшие 5 бит слова (0-23),
mmmmmm - минуты, 6 бит внутри слова (0-59),
sssss - секунды пополам, младшие 5 бит, (счет с точностью до 2 секунд, значение от 0 до 29)

Чтобы получить часы, делаем сдвиг вправо на 11 бит и получим число в младших битах слова
Чтобы получить минуты, сначала выделяем нужные биты, накладаем маску 07e0h и сдвигаем вправо на 5 бит (можно и наоборот, сначала сдвинуть, потом наложить маску 003fh)
Чтобы получить секунды, накладываем маску 001fh и умножаем на 2 (сдвигом влево на 1 бит)

2) InData является частью буфера, который используется при вводе при помощи функции 0ah:
[code lang=asm]InBuffer db 128 ;длина буфера
InCount db ? ;длина реально введенный данных
InData db 128 dup (?) ;сами введенные данные[/code]
Дату програма ждет в виде DD.MM.YYYY, т.е.
день, как два байта в начаче буфера InData,
месяц, как два байта, начиная с адреса InData+3,
года, как четыре байта, начиная с адреса InData+6
Аналогично и время ждем в виде ЧЧ:ММ:СС
Поэтому перед вызовом функций GetNum2 и GetNum4 указываем адрес, где лежат введенные числа в виде ASCII символов цифр

3) Функция GetNum2 читает два десятичных разряда числа (предворительно превращая из символов в число) по адресу в SI и формирует из них число, умножая старший разряд на 10 и складывая с младшим
Функция GetNum4 вызывает два раза GetNum2, дабы получить столетие и год, затем столетие умножается на 100 и складывается с годом.

Небольшое замечание, касаемое года. В формате даты год считается от 1980 года (т.е. нулевой год - это год 1980). Поэтому при преобразования туда/сюда отнимается/добавляется 1980.

3) Перед подсчетом размера программы необходимо понимать следующее:
- для модели tiny данные размещаются за кодом, т.е. последний использованный адрес будет в конце сегмента данных
- сегменты выравниваются на границу слова. Т.е. сегмент данных будет с четного адреса. Другими словами, если длина сегмента кода нечетная, то будет добавлен один байт для выравнивания до четного адреса.
Итак, нам надо добавить две метки:[code lang=asm]InData db 128 dup (?)
FINISH label byte ;метка в конце сегмента данных[/code] и [code lang=asm]GetNum2 endp
FINISH_PRG: ;метка в конце сегмента кода
END BEGIN[/code]Тогда длина программы получается, как:[code lang=asm]mov ax, ((FINISH_PRG-BEGIN+1) and 0fffeh)+FINISH-filename[/code]Т.е. считаем длину сегмента кода (с учетом выранивания!) и длину сегмента данных.
Может возникнуть вопрос: почему нельзя mov ax, FINISH-BEGIN? Нельзя, т.к. в разных сегментах.
И еще вопрос: как вывести полученную длину? Или как hex, либо как десятичное число. Оставляю на самостоятельную работу

Есть еще вариант: разместить данные в сегменте кода за кодом (для модели tiny так делается весьма часто).
Тогда, поставив метку за данными, получим длину, как разность между этой меткой и BEGIN
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
403112
9
12.06.2019, 14:46
общий
12.06.2019, 14:55
Адресаты:
Добрый день! Большое спасибо, доработал для программы подсчёт её размера. Изобразил блок-схему, предварительно исключив процедуры (вышло объёмнее, но для построения блок-схемы так легче). Проверьте последовательность выполнения всех этапов, пожалуйста, и в частности момент с некорректным вводом данных. Да и в целом, допускается ли в таком варианте изображать блок-схему? Спасибо!

давно
Посетитель
7438
7205
12.06.2019, 14:50
общий
Адресаты:
Ну так показуем все, что сделано
Если надо, для загрузки на нащ сервер есть сервис мои файлы
Загружаем, кидаем сюда BBCode-ссылочку вида [url=https://rfpro.ru/d/...
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
7438
7205
12.06.2019, 14:56
общий
Адресаты:
А ссылки на картинки надо давать в виде [ img ]https://rfpro.ru/d/11605.jpg[/img ] (без пробелов)
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
403112
9
12.06.2019, 14:57
общий
Адресаты:


Не сразу разобрался

Изображение
давно
Посетитель
7438
7205
12.06.2019, 14:59
общий
Адресаты:
Удобнее сразу показать...
Хорошо, смотрим блок-схему...
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
7438
7205
12.06.2019, 15:03
общий
Адресаты:
В целом, должно сойти. Может, разве что добавить открытие файла, перед проверкой, что он открыт.
И где вывод размера программы? Раз добавлено, надо учесть и в блок-схеме
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
7438
7205
12.06.2019, 15:07
общий
12.06.2019, 15:08
Адресаты:
Еще кое-что надо подправить:
Параллелограмм "Завершение программы" лишний.
И если введенные дата и время некорректны, то надо перейти на вывод сообщения об ошибке.
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
403112
9
12.06.2019, 15:24
общий
Адресаты:

Спасибо, исправим!
А вот насчёт "некорректных даты и времени": в таком случае по программе идут команды stc и ret, без сообщения. Или я чего-то не понимаю?
давно
Посетитель
7438
7205
12.06.2019, 17:27
общий
Адресаты:
А после вызова подпрограммы идет проверка флага С
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
403112
9
13.06.2019, 19:07
общий
Адресаты:
Сегодня задали вопрос, который поставил меня в ступор: что такое и для чего предназначен PSP (префикс программного сегмента)? В интернете информация дана в какой-то размытой формулировке, мало что понял. Можете прояснить этот момент, пожалуйста?
давно
Посетитель
7438
7205
13.06.2019, 20:07
общий
13.06.2019, 20:55
Адресаты:
Что именно непонятно?
В двух словах:
1) Перед загрузкой программы в первые 100h выделенного сегмента памяти ОС записывает некоторую информацию.
Адрес сегмента его будет в DS и ES. Если программа формата COM, то все будет в одном сегменте и программа будет сразу за PSP.
И, кстати, поэтому начальное смещение у COM обязано быть равным 100h
2) Многие поля PSP устарели, но некоторые используются до сих пор.
Например, по смещению 0 находится код команды int 20h. Что можно использовать, например, если в начале программы отправить в стек 0, затем завершить командой ret.
Более важной информацией, которая находится в PSP, является строка параметров, которая пишется после имени программы. Находится по смещению 80h
Формат, как для команды 0ah. Отсюда ограничение - длина параметров не более 126 байт (плюс в конце код 0dh и первым байтом реальная длина строки)
А! Еще полезной информацией является поле адреса сегмента окружения(где разные там PATH и т.п.) Сегмент окружения создается перед выделением памяти для программы. А его сегмент записывается в поле 2ch. Сейчас уже не сильно актульно, пишутся больше тесты. А раньше использовалось весьма активно.
Там еще есть адреса обработчиков прерываний int 22h, int 23h, int24h, которые тоже практически не используются. Тем более их всегда можно получить при помощи функции 25h
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
403112
9
13.06.2019, 20:35
общий
Адресаты:
Спасибо! Прочитал, что область PSP хранится в памяти с нулевого смещения. Стало быть, адрес и смещение это одно и то?
давно
Посетитель
7438
7205
13.06.2019, 20:53
общий
адрес и смещение это одно и то?
Условно можно считать одним и тем же. Нестрого.
Вообще говоря, адрес в ДОСе - это 20 битное значение, которое получается из пары сегмент:смещение.
В пределах одного сегмента можно считать адрес = смещение, а сегмент как бы подразумевается
Все же, лучше использовать термин "смещение" в пределах сегмента, дабы не было путаницы
Я говорил выше "адрес" некорректно
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
7438
7205
13.06.2019, 20:56
общий
Адресаты:
Подправил свой пост...
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
403112
9
13.06.2019, 21:02
общий
Адресаты:
Вот оно что! Побудили больше разобраться по данной теме, есть к чему стремиться
Форма ответа