Консультация № 186027
14.05.2012, 14:56
100.51 руб.
0 20 2
Добрый день уважаемые эксперты! Прошу помочь в написании программы : Дана целочисленная матрица в байтовом формате по строкам. Все операции обработки матрицы реализовать только циклами. При необходимости использовать макросы. Размерность 4х4. Транспонирование матрицы.
Прошу как можно подробнее написать коментарии к программе.
Модель памяти - .model small
Заранее благодарен.

Обсуждение

давно
Старший Модератор
31795
6196
14.05.2012, 17:50
общий
это ответ
Здравствуйте, Посетитель - 388022!

Смотрите приложение.
Компилятор TASM 2.0.
Обращаю Ваше внимание, что транспонирование указателя на матрицу неуниверсальное и подходит только под матрицы с размером равным степеням двойки, т.е. 2,4,8 и т.д.

вопросы задавайте в мини-форум.
Удачи!

Приложение:
model small
.186
.code
begin:
;Адресуем сегментные регистры
mov ax,@data
mov ds,ax
mov es,ax
;Выводим исходную матрицу
call OutMatrix
;Транспонируем матрицу
call TranspMatrix
;Выводим полученный результат
call OutMatrix
;Ожидаем любую клавишу и завершаем программу
xor ax,ax
int 16h
mov ax,4c00h
int 21h
;
;Вывод матрицы
;устанавливаем внешний индекс цикла
OutMatrix: mov cx,4
;сбрасываем указатель в матрице
xor si,si
;запоминаем его в стеке
OM1: push cx
;устанавливаем внутренний индекс цикла
mov cx,4
;считываем значение числа
OM2: mov al,[Matrix+si]
;переходим к следующему числу
inc si
;выводим число на экран
call OutNumber
;проверяем внутренний цикл
loop OM2
;переходим на новую строку
call NewLine
;востанавливаем внешний индекс цикла
pop cx
;проверяем внешний индекс цикла
loop OM1
;переходим на новую строку
NewLine: mov al,10
int 29h
mov al,13
int 29h
;возвращаемся из под програмы
ret
;
;Выводим одно число в АХ
;запоминаем счетчик цикла в стеке
OutNumber: push cx
;система счисления, в данном случае 10-ая
mov bx,10
;сбрасываем старшую часть слова
xor ah,ah
;сбрасываем счетчик цифр
xor cx,cx
;сбрасываем остаток
ON1: xor dx,dx
;длелим на систему счисления и получаем остаток от деления
div bx
;запоминаем остаток в стеке
push dx
;считаем запомненую цифру
inc cx
;посторяем пока АХ больше нуля
or ax,ax
jnz ON1
;форматируем вывод чисел
;значение, которое при +30Н даст код пробела
mov ax,-10h
;увеличиваем счетчик цифр в стеке
ON2: inc cx
;запоминаем в стеке условный код пробела
push ax
;повторяем пока в стеке не будет 4-е цифры
cmp cx,4
jnz ON2
;выводим число на экран
;извлекаем из стека очередную цифру
ON3: pop ax
;переводим в символьный код для вывода на экран
add al,30h
;выводим на экран символ
int 29h
;повторяем пока в стеке есть цифры
loop ON3
;востанавливаем счетчик цикла
pop cx
ret
;
;трнспонируем матрицу
;устанавливаем индекс внешнего цикла
TranspMatrix: mov cx,4
;сбрасываем указатель в матрице
xor si,si
;сохраняем индекс внешнего цикла
TM1: xchg dx,cx
;устанавливаем индекс внутреннего цикла
mov cx,4
;проверяем индексы циклов
TM2: cmp cx,dx
;если внешний больше или равен внутреннему пропускаем
jbe TM3
;транспонируем указатель на элемент матрицы - строку
mov di,si
;получаем младшие биты
and di,03h
;умножаем их на 4-е
shl di,2
;транспонируем указатель на элемент матрицы - столбец
mov ax,si
;делим на 4-е
shr ax,2
;суммируем новое транспонированное значение указателя
add di,ax
;читаем число из матрицы
mov al,[Matrix+si]
;обмениваем его с числом по транспонированному адресу указателя
xchg [Matrix+di],al
;записываем транспонированное значение
mov [Matrix+si],al
;следующий элемент матрицы
TM3: inc si
;проверяем индекс внутреннего цикла
loop TM2
;востанавливаем значение индекса внешнего цикла
xchg dx,cx
;проверяем индекс внешнего цикла
loop TM1
;выход из подпрограммы
ret
;сегмент данных
.data
Matrix db 01,02,03,04
db 05,06,07,08
db 09,10,11,12
db 13,14,15,16
;сегмент стека
.stack 100h
;точка входа в программу
end begin
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

давно
Посетитель
7438
7205
14.05.2012, 18:45
общий
это ответ
Здравствуйте, Посетитель - 388022!
Альтернативная подпрограмма отработки транспонирования матрицы
в ответе Константина Николаевича (можно просто заменить)
Данная подпрограмма не зависит от размерности матрицы, т.е. N может быть любым

Работает следующим образом: "пробегаем" элементы верхнего треугольника, используя
два регистра: bx для адресации строки и si для адресации элемента в строке.
Значение регистра bx используется я проверки конца отработки, а значение регистра si -
для проверки конца строки.
Одновременно с изменением указанных выше регистров для адресации соответствующего
транспонированного элемента соответственно меняется регистр di

Код:
N	equ	4	;размерность матрицы

TranspMatrix:
xor bx,bx ;база строки
xor si,si ;смещение элемента в строке
xor di,di ;смещение транспонированного элемента
TM1: ;цикл по строкам верхнего треугольника
push si ;сохраним индекс в строке
push di ;сохраним смещение транспонированного элемента
TM2: ;цикл по элементам строки верхнего треугольника
inc si ;диагональный элемент обходим
add di,N ;аналогично для второго элемента
cmp si,N ;дошли до края строки?
je TM3 ;да - переходим на следующую

;обмениваем элементы
mov al,[Matrix+bx+si]
xchg [Matrix+di],al
mov [Matrix+bx+si],al

jmp TM2 ;на слеующий элемент в строке
TM3: ;переход на следующую строку
pop di ;восстановим индексы
pop si
add bx, N ;переходим на слеующую строку
inc si ;и смещаемся на одну позицию вправо
;(рассматриваем только верхний треугольник)
add di, N+1 ;смещаемся на одну строку вниз и на одну позицию вправо
;(соответствующая позиция в нижнем треугольнике)
cmp bx, (N-1)*N ;дошли до последней строки?
jne TM1 ;нет - на отраотку строки
ret
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Неизвестный
15.05.2012, 13:40
общий
Адресаты:
Снова здравствуйте!
Вобщем попробовал совместить всё вместе, а так же кое что поменял местами (сделал как показывали в институте), прошу проверить привильно ли я всё сделал, и можно ли было менять местами, как я это сделал, вобщем вот что получилось :
.model small
.186
.stack 100h
.data
Matrix
db 01,02,03,04
db 05,06,07,08
db 09,10,11,12
db 13,14,15,16
.code
begin:

;Адресуем сегментные регистры
mov ax,@data
mov ds,ax
mov es,ax
call OutMatrix ;Выводим исходную матрицу
call TranspMatrix ;Транспонируем матрицу
call OutMatrix ;Выводим полученный результат

;Вывод матрицы
OutMatrix:
mov cx,4 ;устанавливаем внешний индекс цикла
xor si,si ;сбрасываем указатель в матрице
OM1:
push cx ;запоминаем его в стеке
mov cx,4 ;устанавливаем внутренний индекс цикла
OM2:
mov al, [Matrix+si] ;считываем значение числа
inc si ;переходим к следующему числу
call OutNumber ;выводим число на экран
loop OM2 ;проверяем внутренний цикл
call NewLine ;переходим на новую строку
pop cx ;востанавливаем внешний индекс цикла
loop OM1 ;проверяем внешний индекс цикла

;переходим на новую строку
NewLine:
mov al,10
int 29h
mov al,13
int 29h
ret ;возвращаемся из под програмы

;Выводим одно число в АХ
OutNumber:
push cx ;запоминаем счетчик цикла в стеке
mov bx,10 ;система счисления, в данном случае 10-ая
xor ah,ah ;сбрасываем старшую часть слова
xor cx,cx ;сбрасываем счетчик цифр
ON1:
xor dx,dx ;сбрасываем остаток
div bx ;длелим на систему счисления и получаем остаток от деления
push dx ;запоминаем остаток в стеке
inc cx ;считаем запомненую цифру
or ax,ax ;посторяем пока АХ больше нуля
jnz ON1

;форматируем вывод чисел
mov ax,-10h ;значение, которое при +30Н даст код пробела

ON2:
inc cx ;увеличиваем счетчик цифр в стеке
push ax ;запоминаем в стеке условный код пробела
cmp cx,4 ;повторяем пока в стеке не будет 4-е цифры
jnz ON2

;выводим число на экран
ON3:
pop ax ;извлекаем из стека очередную цифру
add al,30h ;переводим в символьный код для вывода на экран
int 29h ;выводим на экран символ
loop ON3 ;повторяем пока в стеке есть цифры
pop cx ;востанавливаем счетчик цикла
ret

;трнспонируем матрицу
;устанавливаем индекс внешнего цикла
TranspMatrix:
xor bx,bx ;база строки
xor si,si ;смещение элемента в строке
xor di,di ;смещение транспонированного элемента
TM1: ;цикл по строкам верхнего треугольника
push si ;сохраним индекс в строке
push di ;сохраним смещение транспонированного элемента
TM2: ;цикл по элементам строки верхнего треугольника
inc si ;диагональный элемент обходим
add di,N ;аналогично для второго элемента
cmp si,N ;дошли до края строки?
je TM3 ;да - переходим на следующую

;обмениваем элементы
mov al,[Matrix+bx+si]
xchg [Matrix+di],al
mov [Matrix+bx+si],al
jmp TM2 ;на слеующий элемент в строке
TM3: ;переход на следующую строку
pop di ;восстановим индексы
pop si
add bx, N ;переходим на слеующую строку
inc si ;и смещаемся на одну позицию вправо
;(рассматриваем только верхний треугольник)
add di, N+1 ;смещаемся на одну строку вниз и на одну позицию вправо
;(соответствующая позиция в нижнем треугольнике)
cmp bx, (N-1)*N ;дошли до последней строки?
jne TM1 ;нет - на отработку строки
ret
;Ожидаем любую клавишу и завершаем программу
xor ax,ax
int 16h
mov ax,4c00h
int 21h
end begin
давно
Старший Модератор
31795
6196
15.05.2012, 13:44
общий
Цитата: 388022
;Адресуем сегментные регистры
mov ax,@data
mov ds,ax
mov es,ax
call OutMatrix ;Выводим исходную матрицу
call TranspMatrix ;Транспонируем матрицу
call OutMatrix ;Выводим полученный результат
Тут Вы теряете управления программой
;Вывод матрицы
OutMatrix:

Верните последние 4-е строчки
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

давно
Старший Модератор
31795
6196
15.05.2012, 13:49
общий
15.05.2012, 14:09
[code h=200]model small
N equ 4
.186
.code
begin:
;Адресуем сегментные регистры
mov ax,@data
mov ds,ax
mov es,ax
;Выводим исходную матрицу
call OutMatrix
;Транспонируем матрицу
call TranspMatrix
;Выводим полученный результат
call OutMatrix
;Ожидаем любую клавишу и завершаем программу
xor ax,ax
int 16h
mov ax,4c00h
int 21h
;
;Вывод матрицы
;устанавливаем внешний индекс цикла
OutMatrix: mov cx,N
;сбрасываем указатель в матрице
xor si,si
;запоминаем его в стеке
OM1: push cx
;устанавливаем внутренний индекс цикла
mov cx,N
;считываем значение числа
OM2: mov al,[Matrix+si]
;переходим к следующему числу
inc si
;выводим число на экран
call OutNumber
;проверяем внутренний цикл
loop OM2
;переходим на новую строку
call NewLine
;востанавливаем внешний индекс цикла
pop cx
;проверяем внешний индекс цикла
loop OM1
;переходим на новую строку
NewLine: mov al,10
int 29h
mov al,13
int 29h
;возвращаемся из под програмы
ret
;
;Выводим одно число в АХ
;запоминаем счетчик цикла в стеке
OutNumber: push cx
;система счисления, в данном случае 10-ая
mov bx,10
;сбрасываем старшую часть слова
xor ah,ah
;сбрасываем счетчик цифр
xor cx,cx
;сбрасываем остаток
ON1: xor dx,dx
;длелим на систему счисления и получаем остаток от деления
div bx
;запоминаем остаток в стеке
push dx
;считаем запомненую цифру
inc cx
;посторяем пока АХ больше нуля
or ax,ax
jnz ON1
;форматируем вывод чисел
;значение, которое при +30Н даст код пробела
mov ax,-10h
;увеличиваем счетчик цифр в стеке
ON2: inc cx
;запоминаем в стеке условный код пробела
push ax
;повторяем пока в стеке не будет 4-е цифры
cmp cx,N
jnz ON2
;выводим число на экран
;извлекаем из стека очередную цифру
ON3: pop ax
;переводим в символьный код для вывода на экран
add al,30h
;выводим на экран символ
int 29h
;повторяем пока в стеке есть цифры
loop ON3
;востанавливаем счетчик цикла
pop cx
ret
;
;трнспонируем матрицу
;устанавливаем размер матрицы
TranspMatrix: mov bx,N
;устанавливаем индекс внешнего цикла
mov cx,N
;сбрасываем указатель в матрице
xor si,si
;сохраняем индекс внешнего цикла
TM1: xchg dx,cx
;устанавливаем индекс внутреннего цикла
mov cx,N
;проверяем индексы циклов
TM2: cmp cx,dx
;если внешний больше или равен внутреннему пропускаем
jbe TM3
;сохраняем используемый регистр
push dx
;готовимся к преобразованиям
mov ax,si
;разделяем строки и столбцы
xor dx,dx
div bx
;АХ - строка
;DX - столбец
;запоминаем строку
mov di,ax
;работаем со столбцом
mov ax,dx
;преобразовываем столбец в указатель
xor dx,dx
mul bx
;получаем транспонированный указатель
add di,ax
;востанавливаем регистр
pop dx
;считываем текущее число
mov al,[Matrix+si]
;обмениваем его с числом по транспонированному адресу указателя
xchg [Matrix+di],al
;записываем транспонированное значение
mov [Matrix+si],al
;следующий элемент матрицы
TM3: inc si
;проверяем индекс внутреннего цикла
loop TM2
;востанавливаем значение индекса внешнего цикла
xchg dx,cx
;проверяем индекс внешнего цикла
loop TM1
;выход из подпрограммы
ret
;сегмент данных
.data
;Matrix db 01,02,03,04
; db 05,06,07,08
; db 09,10,11,12
; db 13,14,15,16
;формируем матрицу
Matrix label byte
i = 1
rept N*N
db i
i = i+1
endm
;сегмент стека
.stack 100h
;точка входа в программу
end begin[/code]
Переделал транспониование с помощью умножения и деления, а не сдвигов.
Добавил единственный оптимальный макрос, т.к. всё остальное только увеличивает длину кода.
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

давно
Посетитель
7438
7205
15.05.2012, 13:57
общий
15.05.2012, 13:59
Верните последние 4-е строчки
Совершенно верно, у Вас получилось, что после второго вызова п/п вывода матрицы
Вы опять попадаете, уже прямо, на нее же и после ret улетаете неизвестно куда...
А надо завершить программу...
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
7438
7205
15.05.2012, 14:05
общий
Если хотите, чтобы выход был в конце, не вопрос...
Поставьте после второго вывода матрицы jmp на метку в конце( т.е. на ожидание любой клавиши)
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Старший Модератор
31795
6196
15.05.2012, 14:09
общий
Адресаты:
Цитата: Лысков Игорь Витальевич
Поставьте после второго вывода матрицы jmp на метку в конце

Зачем?
Проще все подпрограммы перенести в начало, перед меткой begin
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

давно
Посетитель
7438
7205
15.05.2012, 14:10
общий
Адресаты:
Зачем? Проще все подпрограммы перенести в начало, перед меткой begin
Да, конечно
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
давно
Посетитель
7438
7205
15.05.2012, 14:41
общий
Как успехи? Вам наших комментариев достаточно?
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Неизвестный
15.05.2012, 14:58
общий
Останусь на своём варианте, так мне легче воспринимать и понимать программу, как я понял если верну 4 строчки :
;Ожидаем любую клавишу и завершаем программу
xor ax,ax
int 16h
mov ax,4c00h
int 21h
то всё остальное будет правильно? Может быть глупый вопрос но не понимаю почему по порядку не пишется, допустим: "сначала задаём сегмент стека, формируем матрицу ... трансопируем ... и в конце уже соответственно код завершения программы". В данном случае всё немного наоборот.
Неизвестный
15.05.2012, 15:12
общий
Я плоховато пока разбираюсь в ассемблере. Может это неправильно но мне проще воспринимать когда всё по порядку, если можно то прошу подправить в моём варианте так чтобы было правильно .
Заранее большое спасибо!)
давно
Старший Модератор
31795
6196
15.05.2012, 15:24
общий
Сегменты стека, данных и кода Вы можете распологать в любой последовательности и в лобом количестве. К примеру даже так:
.data
.stack
.code
.data
.code
.data
Компилятор при комппановке программы сам соберет одноименные сегменты одной группы, т.е. в результирующем коде они будут располагатся в одном сегменте. Аналогично происходит и с кодом. Но если данные могут быть разбросанны и не систематизированны. Код представляет собой определенный программистом последовательный набор команд процессора, которые выполняют определенный алгоритм заложенный в программу. Любое нарушения этой последовательности приводит к сбоям и т.д.
Цитата: 388022
прошу подправить в моём варианте так чтобы было правильно

К примеру так будет правильно:

[code h=200].model small
.186
.stack 100h
.data
Matrix
db 01,02,03,04
db 05,06,07,08
db 09,10,11,12
db 13,14,15,16
.code

;Вывод матрицы
OutMatrix:
mov cx,4 ;устанавливаем внешний индекс цикла
xor si,si ;сбрасываем указатель в матрице
OM1:
push cx ;запоминаем его в стеке
mov cx,4 ;устанавливаем внутренний индекс цикла
OM2:
mov al, [Matrix+si] ;считываем значение числа
inc si ;переходим к следующему числу
call OutNumber ;выводим число на экран
loop OM2 ;проверяем внутренний цикл
call NewLine ;переходим на новую строку
pop cx ;востанавливаем внешний индекс цикла
loop OM1 ;проверяем внешний индекс цикла

;переходим на новую строку
NewLine:
mov al,10
int 29h
mov al,13
int 29h
ret ;возвращаемся из под програмы

;Выводим одно число в АХ
OutNumber:
push cx ;запоминаем счетчик цикла в стеке
mov bx,10 ;система счисления, в данном случае 10-ая
xor ah,ah ;сбрасываем старшую часть слова
xor cx,cx ;сбрасываем счетчик цифр
ON1:
xor dx,dx ;сбрасываем остаток
div bx ;длелим на систему счисления и получаем остаток от деления
push dx ;запоминаем остаток в стеке
inc cx ;считаем запомненую цифру
or ax,ax ;посторяем пока АХ больше нуля
jnz ON1

;форматируем вывод чисел
mov ax,-10h ;значение, которое при +30Н даст код пробела

ON2:
inc cx ;увеличиваем счетчик цифр в стеке
push ax ;запоминаем в стеке условный код пробела
cmp cx,4 ;повторяем пока в стеке не будет 4-е цифры
jnz ON2

;выводим число на экран
ON3:
pop ax ;извлекаем из стека очередную цифру
add al,30h ;переводим в символьный код для вывода на экран
int 29h ;выводим на экран символ
loop ON3 ;повторяем пока в стеке есть цифры
pop cx ;востанавливаем счетчик цикла
ret

;трнспонируем матрицу
;устанавливаем индекс внешнего цикла
TranspMatrix:
xor bx,bx ;база строки
xor si,si ;смещение элемента в строке
xor di,di ;смещение транспонированного элемента
TM1: ;цикл по строкам верхнего треугольника
push si ;сохраним индекс в строке
push di ;сохраним смещение транспонированного элемента
TM2: ;цикл по элементам строки верхнего треугольника
inc si ;диагональный элемент обходим
add di,N ;аналогично для второго элемента
cmp si,N ;дошли до края строки?
je TM3 ;да - переходим на следующую

;обмениваем элементы
mov al,[Matrix+bx+si]
xchg [Matrix+di],al
mov [Matrix+bx+si],al
jmp TM2 ;на слеующий элемент в строке
TM3: ;переход на следующую строку
pop di ;восстановим индексы
pop si
add bx, N ;переходим на слеующую строку
inc si ;и смещаемся на одну позицию вправо
;(рассматриваем только верхний треугольник)
add di, N+1 ;смещаемся на одну строку вниз и на одну позицию вправо
;(соответствующая позиция в нижнем треугольнике)
cmp bx, (N-1)*N ;дошли до последней строки?
jne TM1 ;нет - на отработку строки
ret


begin:
;Адресуем сегментные регистры
mov ax,@data
mov ds,ax
mov es,ax
call OutMatrix ;Выводим исходную матрицу
call TranspMatrix ;Транспонируем матрицу
call OutMatrix ;Выводим полученный результат

;Ожидаем любую клавишу и завершаем программу
xor ax,ax
int 16h
mov ax,4c00h
int 21h
end begin[/code]
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

давно
Старший Модератор
31795
6196
15.05.2012, 15:47
общий
15.05.2012, 15:50
Цитата: 388022
сделал как показывали в институте

Вы можете выложить на портале методичку, по которой учитесь?
Игорь Витальевич и я переделаем код под Вашу учебную прогамму.
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

давно
Посетитель
7438
7205
15.05.2012, 15:52
общий
Вы можете выложить на портале методичку, по которой учитесь?
Во! Это правильно! Посмотрим, что от Вас требуют, и объясним, что Вы неправильно поняли
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Неизвестный
15.05.2012, 16:09
общий
Методички к сожалению никакой нет, одни только лекции.
Последний вариант программы вполне устраивает. Надеюсь преподователя тоже устроит.
Ещё раз большое спасибо!
Неизвестный
23.05.2012, 12:55
общий
Адресаты:
Уважаемые эксперты прошу помочь переделать программу без прерываний 30h и 29h, или подробно описать что каждое из них делает и на что ссылается. Преподаватель сказал что незнает что делает 30h)) Прошу переделать или обьяснить как можно подробнее и проще.
Заранее большое спасибо!
Если надо могу создать новый вопрос.
Программу сдавал кот. в ответе №3.
давно
Посетитель
7438
7205
23.05.2012, 13:01
общий
И где Вы увидели прерывание 30h? Посмотрите внимательнее, нету там такого...
А прерывание 29h выводит на экран в текущей позиции символ из al.
Аналог - функция 02 прерывания 21h, только там символ должен быть в dl
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Неизвестный
23.05.2012, 13:23
общий
Понятно спасибо!
А для чего служит 30h поясните попобробней пожалуйста. Не понимаю всей этой операции - когда форматируем вывод чисел и выводим число на экран)
давно
Посетитель
7438
7205
23.05.2012, 13:30
общий
Используется при преобразовании двоичного числа в строку цифр.
Сначала при помощи деления на 10 получаются разряды 0-9,
затем эти числа преобразуются в символы '0'-'9', которые имеют код 30h-39h,
т.е. отличаются на 30h. Вот мы эти 30h и добавляем
Об авторе:
"Если вы заметили, что вы на стороне большинства, —
это верный признак того, что пора меняться." Марк Твен
Форма ответа