Правильно отображать вывод сообщения, используя одну переменную, сохраняя их положение - PullRequest
0 голосов
/ 15 ноября 2018

Добрый день, уважаемые программисты,

Наш учитель назначил нас для создания программы обратного отсчета от 5 до 0.
Большинство моих одноклассников используют много переменных для отображения выходных данных. Он работает, кодируя его, как показано ниже приведенного кода.

Проблема в том, что код выглядит ужасно и создает слишком много дополнительных LOC.

Проблема в том, как я могу сократить свой код?

Мне не нравится использовать много переменных для этого, вызывать каждую переменную одну за другой и вызывать позицию курсора слишком много раз. Я публикую только часть кода, потому что код слишком длинный.

Это мой код

csrPos macro x1:REQ, y1:REQ
       mov ah, 02h
       mov bh, 0
       mov dl, x1
       mov dh, y1
       int 10h
       endm

prnstr macro msg
    mov ah, 9
    mov dx, offset msg
    int 21h
    endm

.model small
.stack 100h
.data
       ; Five message with light color green
       msgFive0 db 27, "[2;32;40m$"
       msgFive1 db "ÛÛÛÛÛ$"
       msgFive2 db "ÛÛ   $"
       msgFive3 db "ÛÛ   $"
       msgFive4 db "ÛÛÛÛ $"
       msgFive5 db "   ÛÛ$"
       msgFive6 db "   ÛÛ$"
       msgFive7 db "ÛÛ ÛÛ$"
       msgFive8 db "ÛÛ ÛÛ$"
       msgFive9 db " ÛÛÛ $"
.code
       mov ax, @data
       mov ds, ax

       csrPos 38, 7
       prnstr msgfive0
       csrPos 38, 8
       prnstr msgfive1
       csrPos 38, 9
       prnstr msgfive2
       csrPos 38, 10
       prnstr msgfive3
       csrPos 38, 11
       prnstr msgfive4
       csrPos 38, 12
       prnstr msgfive5
       csrPos 38, 13
       prnstr msgfive6
       csrPos 38, 14
       prnstr msgfive7
       csrPos 38, 15
       prnstr msgfive8
       csrPos 38, 16
       prnstr msgfive9

       mov ah, 4ch
       int 21h

END

Выход:
Это вывод этой программы, когда я ее запускаю. Это уже выглядит хорошо, как сказал нам наш учитель. Пять выходных

То, что я хочу сделать, это так.

.data
       ; Five message with light color green
       msgFive db 27, "[2;32;40m$"
                db "ÛÛÛÛÛ$"
                db "ÛÛ   $"
                db "ÛÛ   $"
                db "ÛÛÛÛ $"
                db "   ÛÛ$"
                db "   ÛÛ$"
                db "ÛÛ ÛÛ$"
                db "ÛÛ ÛÛ$"
                db " ÛÛÛ $"


.code
       mov ax, @data
       mov ds, ax

       csrPos 38, 7
       prnstr msgfive

       mov ah, 4ch
       int 21h
END

Как я могу сделать это, сохраняя вывод с использованием этого кода?

Извините за мой плохой английский. Я использую ассемблер TASM

1 Ответ

0 голосов
/ 15 ноября 2018

Что ж, ваша общая идея вроде как хорошая, но вы не объясняете, с какой проблемой вы столкнулись, чтобы не реализовать ее.

Мне кажется, что одним из PITA будет этот макрос csrPos 38, 7 (Я действительно ненавижу макросы, особенно в коде людей, которые только изучают ассемблер, так что воспринимайте это как предвзятое мнение, но на самом деле ... я попытаюсь объяснить также на самом деле, так что вы можете решить сами).

Это мгновенно перемещает позицию вывода для следующего вывода, но затем вы печатаете символ как серию многократных выводов, поэтому вам нужно настроить положение между ... но этот предложенный код prnstr msgfive не имеет простого представления, где предыдущий * Макрос 1007 * настроил его. Вы можете решить это внутри prnstr с помощью службы BIOS, чтобы фактически прочитать текущую позицию перед печатью, распечатать символы, откорректировать положение и выполнить csrPos ... эта идея должна сразу почувствовать себя «запахом кода» (хотя если у вас нет лучшей идеи, это часто помогает написать это, по крайней мере, «неправильным» способом, чтобы вы могли сами увидеть полученный источник и снова подумать об этом с новым опытом, который часто помогает найти лучшее решение).

То, что вам, вероятно, нужно, это что-то вроде prnstr msgfive, 38, 7, позволяющее prnstr обрабатывать само положение и удаляющее csrPos 38, 7 из основной части кода ..., которое также сэкономит вам 1LOC, а также удалит это столкновение ответственности за информацию, где prnstr не знал, где он должен его распечатать.

Но тогда я бы пожертвовал несколькими LOC исходного кода, чтобы полностью избежать макроса, и скорее сделал бы что-то вроде (например, также не стесняйтесь выбирать другой способ предоставления аргументов, например, разные регистры или даже соглашение о вызовах на основе стека, если хотите):

    ...
    mov  dx,38 + 7*256        ; position[38,7]
    mov  si,OFFSET char_five  ; memory address of font data
    mov  bl,10                ; light green color
    call print_char           ; call subroutine to output the character
    ...

Теперь подпрограмма print_char, выполняющая всю тяжелую работу, обладает всей важной информацией, поэтому она сама может решить, когда / как она изменит выходную позицию, и задача отображения некоторого символа теперь составляет всего 4 LOC в основной блок, который является разумным ИМО.


Как вы можете получить хорошие идеи о том, как сократить свой код ... ну, если вы посмотрите на свой оригинальный код (в идеале в режиме дизассемблирования в отладчике), вы увидите серию одних и тех же инструкций, повторяющихся снова и снова просто с разными непосредственными значениями. Это всегда признак того, что вы можете переместить эти непосредственные данные в некую структуру данных и написать код только один раз, используя своего рода «цикл», чтобы повторить его с несколькими данными.

Таким образом, вы должны искать важные части вашего кода, которые отличаются от других и продолжают повторяться - если это больше 3 раз, вероятно, стоит преобразовать его в подпрограмму.

Также вам следует часто пытаться искать непосредственные данные, которые можно реально рассчитать - например, если вы хотите напечатать простые числа от 2 до 1000, вы можете либо поместить полный список в виде текста в источник (и он будет самым быстрым решение!), или вы можете запрограммировать его в виде цикла от целого числа 2 до 1000, проверяя каждый, является ли оно простым, и выводить его (меньший двоичный файл для цены вычисления).

Некоторые данные часто проще вычислить, чем записать вручную в источнике (например, «положение курсора для блока строк, начинающегося в столбце 38), и падение производительности часто незначительно (как в вашей текущей задаче, вам следует с задержкой в ​​1 секунду, поэтому неважно, отображаете ли вы цифру в 10 микросекунд или 15 микросекунд).

Другие связанные с ASM способы сохранения некоторых LOC или упрощения обслуживания:

  • если вы знаете, что ваша подпрограмма должна будет поместить позицию в dh и dl, и вы можете использовать свое собственное соглашение о вызовах, то нет причин не передавать аргумент напрямую в dx, поэтому подпрограмме не нужно копировать / перемещать значения аргумента в dh:dl.

  • использовать подпрограммы вместо макросов, это упрощает просмотр исходного кода для других и облегчает отладку, поскольку то, что вы видите в отладчике, намного больше похоже на то, что вы написали в исходном коде.Когда вы отлаживаете макросы, вы видите их в отладчике во внедренном виде, то есть он совсем не похож на источник, что делает его более запутанным (как только вы попадете в особую ситуацию, где использование макроса имеет смысл, вы узнаете его, но в этот момент ваши макросы скорее похожи на подпрограммы и должны быть написаны так).(также, когда опытный программист просматривает ваш код, он знает, что делает mov ax,10h, но он не знает, что делает csrPos, потому что это не инструкция x86, поэтому им приходится переключаться назад и вперед между кодом, который он просматривает, иопределение макроса, потому что они не запомнят его мгновенно, но они должны каждый раз учитывать каждую инструкцию, скрытую в макросе, так как они могут иметь нежелательные побочные эффекты для вашего другого кода, поэтому они не могут просто походить на «хорошо, это простопоменяйте позицию, не нужно беспокоиться о инструкциях "... и вы сами будете смущены, когда через несколько месяцев вы проверите этот источник)

Вы можете проверить мой ответ на несколько других-схожий вопрос здесь Положение курсора TASM , чтобы увидеть конкретный способ реализации аналогичной подпрограммы print_char, используя циклы и определения данных, чтобы сделать код более «общим», подходящим для печати «любого» символа.Это делает саму подпрограмму намного более сложной, чем ваша последовательность pos, print, pos, print, pos, print, ..., но вы должны написать ее только один раз, отладить только сто раз, а затем вы можете использовать ее для всех символов.Кроме того, это веселее, даже если это займет больше времени.И вы можете похвастаться, что вы более изощренный программист, чем кто-либо, использующий эту утомительную ручную последовательность написания всего ...;): D

...