Я не знаю, как для увеличения значения на каждую линию.
Ну, за каждую строку делать inc where-the-value-is-stored
(либо иметь его в каком-либо запасном регистре, либо в памяти, если у вас закончились запасные регистры).
Держите в голове или в резюме комментариев, каково ваше текущее распределение регистров, поэтому вы знаете, что является запасным или уже используется для чего-то.
Убедитесь, что вы выполняете требования внешних вызовов, так как вы можете легко выбрать другой регистр для своего собственного кода, но вы не можете изменить, например, int 21h
, чтобы принять служебный номер в bh
, поскольку он уже реализован ваш поставщик DOS, чтобы принять номер службы в ah
. Поэтому либо избегайте использования ah
, либо используйте шаблон сохранения/восстановления (см. Ниже).
Постарайтесь, чтобы простые простые вещи, такие как добавочное значение, равно inc
. Сборка на самом деле неплохо в этом случае, если вы сохраняете четкое представление о том, что хотите в своей голове, с точки зрения очень простых числовых операций/шагов, вы обычно можете найти довольно простое и простое сочетание инструкций ASM, выполняющих именно это и не очень остальное. Довольно часто ничего больше.
Если у вас есть трудное время для сопоставления некоторого желания нескольким инструкциям по сборке простым способом, ваша задача высокого уровня, вероятно, не будет достаточно разбита на простые шаги, поэтому попробуйте разбить ее немного больше, а затем попробуйте еще раз найти некоторые короткий простой перевод в инструкции.
loop rel8
является одним из немногих более сложных инструкций x86, делая в основном это:
dec cx (ecx in 32b mode, rcx in 64b mode)
jnz rel8
Но это не повлияет на флаги (dec + jnz
происходит внутренне как одной специализированной вещи, не в буквальном смысле, как два оригинальные инструкции dec + jnz
), и он искусственно замедляется на современных процессорах x86, чтобы помочь немного устаревшему SW, который использовал пустые loop $
циклы для создания «задержек» (это бесполезно, поскольку для этого SW оно слишком быстро, и оно «удаляет», иначе очень хороший код операции для будущего SW: /).
Таким образом, вы можете предпочесть настоящие две инструкции «dec cx
jnz rel8
» для реального программирования, это будет иметь лучшую производительность на современном процессоре x86.
В сборке Регистры CPU подобны «супер-глобальным», т.е. есть один cx
на ядро центрального процессора (внутренне это не так для современных x86, но это то, как он выглядит с внешней стороны, с точки зрения программиста).
Так что если вам нужны два разных значения, например counter1 и counter2, вам нужно будет написать дополнительный дополнительный код, сохраняя при этом нужное значение cx
и загружая другое по необходимости.
Например два вложенных цикла осуществляется loop
:
mov cx,10
outer_loop:
mov bx,cx ; preserve outer counter in bx
mov cx,5
inner_loop:
; some loop code
loop inner_loop
mov cx,bx ; restore outer counter
loop outer_loop
Или, если вам не хватает запасных регистров, вы можете использовать стек, "наивное" путь:
mov cx,10
outer_loop:
push cx ; preserve outer counter
mov cx,5
inner_loop:
; some loop code
loop inner_loop
pop cx ; restore outer counter
loop outer_loop
(C++ компиляторы разрешат это по-разному, выделяя локальную переменную в пространстве стека, поэтому вместо push/pop он будет использовать одно и то же пятно памяти на [sp+x]
или [bp-x]
напрямую, экономя производительность, не корректируя sp
с e очень полезно, например push/pop
.))
Но если вы посмотрите на предыдущую часть моего ответа, вы сможете найти другой способ решения вложенных циклов с двумя счетчиками - без дополнительных инструкций сохранения/восстановления.
Но картина сохранения/восстановления значения в конкретном целевом регистре является то, что вы должны полностью понимать и быть в состоянии использовать во всех видах различных ситуаций (даже если это не требуется для вложенных циклов), для Например, если вы прочтете документацию о ah=2, int 21h
, вы увидите, что она заботится только о ah
и dl
значениях (и изменяет al
). Так, например, dh
является «запасным».
Затем, если вы хотите выводить два символа: A
и пространство, но вы все еще хотите, чтобы закончить с A
в главном «переменной» (будет dl
в следующем примере), вы можете сделать это:
init_part:
mov dx,' '*256 + 'A' ; dh = ' ', dl = 'A'
mov ah,2 ; output single char service
; some other init code, etc..
inner_part_somewhere_later:
int 21h ; output dl to screen (initially 'A')
xchg dl,dh ; preserves old "dl" and loads "dh" into it (swaps them)
int 21h ; output dh to screen (space)
xchg dl,dh ; restores 'A' in dl
; so *here* you can operate with 'dl'
; as some inner_part loop "variable"
; modifying it for another inner_part iteration
Наконец, если у вас есть задача, подобная вашей, и решение не является очевидным, одним из этапов рассуждения может быть «откат», что вы хотите.
Вы знаете, что вы хотите вывод на экран (<NL>
= новая строка):
1<NL>
1 2<NL>
Так себе представить, что это означает, что на нижнем уровне, в конце концов. Конечно, есть несколько способов его достижения (включая запись целых строк, подготовленных в буфере памяти, вместо одиночных символов), но если я буду придерживаться вашего одиночного выходного сигнала, этот желаемый вывод переводится в эту потребность:
для того, чтобы позвонить int 21h, ah=2
с dl
набор для:
[49
(цифра 1), 13
(возврат каретки), 10
(строки), 49
, 32
(пробел), 50
(цифра 2), 13
, 10
].
Это не выглядит «зацикленным», но если вы добавите больше строк, появится образец «цифра + пробел» для внутреннего цикла. Вы также можете «обмануть» бит и вывести одно бесполезное пространство после последней цифры, так как для обычного пользователя он будет «невидим». В этот момент вы должны быть в состоянии «вернуться назад» на этот высокий уровень дизайна:
char_per_line_count = 1
ending_char_count = 2
[lines_loop:
char = '1'
line_counter = char_per_line_count
[chars_loop:
int 21h,2 with char
int 21h,2 with space
loop to chars_loop while (--line_counter)]
int 21h,2 with 13
int 21h,2 with 10
++char_per_line_count
loop to lines_loop while (char_per_line_count < ending_char_count)]
Теперь вы можете попробовать запустить его несколько раз в вашей голове, чтобы проверить выход действительно то, что вы хотите.
Как только вы получите такой высокий уровень обзора, как вы можете достичь желаемого результата, вы можете начать искать способ, как хорошо реализовать определенные шаги.
Если вы понимаете каждую предыдущую часть этого ответа, я думаю, что этот алгоритм будет легко переписать в инструкции ASM. Просто оставляйте комментарии в коде перед определенной группой инструкций.
Затем, когда вы отлаживаетесь из-за некоторой ошибки, вы можете легко сравнить то, что код действительно делает с комментарием, что он должен был сделать, найти расхождение и исправить его.
Но все время главное, чтобы сравнить ваш код с тем, что конечный результат на экране определен, всякий раз, когда вы застреваете, сравните свой текущий вывод с желаемым, найдите какое-то несоответствие, оцените, какой из них выглядит самым простым для исправления , и попытайтесь его исправить. Если больше не найдено расхождений, вы как бы «сделали», хотя я бы настоятельно предложил еще раз взглянуть на ваш код, не упростится ли его, и если он корректно работает для угловых шкафов (например, " что произойдет, если пользователь вводит букву вместо цифры «).
Не важно иметь код, который правильно обрабатывает каждый угловой футляр, но вы должны знать, что произойдет в каждом случае, и решить, является ли это «достаточно хорошим» или нет (как правило, мусор в -> вывоз мусора "в порядке," мусор в -> сбой или повреждение данных "не круто," мусор в -> значимое исправление или сообщение об ошибке прохладно).
Должен ли второй источник каким-то образом отличаться? Он выглядит так же, как и для меня. – Ped7g
У вас * есть * для использования внутренней петли для каждой строки. Вы можете либо сохранить CX (push/pop) из внешнего цикла, либо использовать какой-либо другой регистр и имитировать 'loop' с' dec' + 'jnz'. –