2016-10-31 6 views
1

Я пытаюсь создать программу на языке ассемблера, основанную на теории «3n + 1», где, если число нечетное, умноженное на 3 и добавление 1 или даже четное, разделите на 2 и продолжайте движение до тех пор, пока не будет достигнуто число 1. Программа запрашивает у пользователя положительное целое число, а затем запускает программу 3n + 1, и как только она достигает 1, она возвращается к началу (еще не совсем поняла шляпу, так как я продолжаю получать сообщение об ошибке «относительный прыжок из диапазона на 0021h» байт "и чтобы программа останавливалась, когда пользователь вводит 0. во время работы программы, она должна распечатывать каждое число в последовательности (включая введенную пользователем) и продолжать работу до тех пор, пока она не напечатает 1. Проблема что я проверил мой код много раз и успешно собрал его в DosBox с помощью команды TASM без ошибок или предупреждений, но когда я запускаю его, программа, кажется, останавливается, и dosbox не дает мне подсказки для команды как это было бы, когда программа выйдет. Это мой код:Программа случайным образом останавливается без видимой причины

INCLUDE io.h 

Cr  EQU 0DH  ; carriage return 
Lf  EQU 0AH  ; line feed 

TheStack SEGMENT STACK 
      DW 100H DUP (?) 
TheStack ENDS 

Data  SEGMENT 
Number Dw ? 
Prompt1 DB 'Please enter the positive integer of your choice: ', 0 
String DB 40 DUP (?) 
New DB 6 DUP (?), 0 
Int2  DB '2' 

Data  ENDS 

Code  SEGMENT 
      ASSUME Cs:Code, Ds:Data 

Start: Mov Ax, SEG Data ; Load Data Segment Number. 
      Mov Ds, Ax 

Prompt: Output Prompt1  ; Prompt for first number. 
      Inputs String, 40 ; Read the ASCII characters.  
     AToI String  
      Mov Number, Ax ;stops here i think 
     atoi int2 
     mov Ax, number 
     xor cx,cx 
loop1: mov bx, 2 
     mov cx, 1 
     mov dx, 3 
     div int2  
     cmp ah,1 
     je isodd 
     cmp ah,0 
     je nextinst 
     xor cx,cx 
nextinst: mov ax, number 
     div bx 
     add ax, cx 
     mov number, ax 
     cmp ax, number 
     Je loop2 
     xor cx,cx 
isodd: mov ax, number 
     mul dx 
     add ax, cx 
     mov number, ax  
     xor cx,cx 
loop2: itoa new, Ax 
     output new 
     mov Ax, number 
      cmp Ax,1 ; Compare cx to the limit 
      jg loop1 ; Loop while less or equal 


Quit:  Mov Al, 0   ; Put return code of zero in Al. 
      Mov Ah, 4CH   ; Put DOS function call in Ah. 
      int 21H    ; Call DOS 

Code  ENDS 
      END Start 

Это просто так расстраивает, как у меня есть все мои петли, казалось бы, настроены правильно и внесли разные изменения в программу в отчаянных попытках заставить ее работать правильно, но безуспешно! Похоже, я бегаю по кругу, чтобы исправить то, что должно быть легко исправить. Что мне нужно сделать, чтобы программа работала правильно? Что в моем коде приведет к тому, что это произойдет? Или это просто проблема с DOSbox?

+0

Есть ли ** номер строки ** для ошибки «относительного перехода ...»? Это 'jg loop1'? –

+1

Вы должны отлаживать это в отладчике, так как многие вещи вас удивят ... (например, 'div int2' не делает то, что вы считаете). – Ped7g

+0

Кстати, программа хотя бы отображает подсказку и вы можете ввести номер, и он показывает некоторые цифры? Из вашего описания неясно, где именно он останавливается. – Ped7g

ответ

0

Во всяком случае, для образовательных целей, основной цикл можно записать так:

; ax = n (unsigned greater than 1) 
Loop3n1: 
    mov  bx,ax  ; copy n to bx 
    shr  ax,1  ; divide by 2 
    jnc  wasEven  ; CF set when odd from shr 
    mov  ax,bx 
    add  ax,bx 
    add  ax,bx  ; ax = 3*n 
    inc  ax   ; +1 
wasEven: 
    ; here ax = 3*n+1 (odd n), or n/2 (even n) 

    ; display ax 
    ; make sure ax value is preserved! 

    cmp  ax,1 
    ja  Loop3n1  ; if (1 < n), loop 

    ; jump to start? (and how it will exit?) 

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

Например, здесь я делясь на 2 и проверяю на равномерность с помощью одного shr ax,1 (целые числа закодированы на компьютере в битах, которые представляют две степени.Так что сдвиг «1» в бит слева или справа делает его эффективным * 2 или/2, плюс наименее значащий бит приземляется в CF, который используется для проверки, если число было четным/нечетным).

Если число было даже, я закончил, просто покажу его и зациклив.

Если это было странно, я восстанавливаю его из копии - это дополнительный материал без формулы, который иногда неизбежен, но вы бы поняли это, пытаясь сделать 3 * n и потеряв значение n (так как это уже n/2).

Затем я делаю 3n + 1 простым n + n + n + 1, чтобы не беспокоиться о mul и сбивать больше регистров. С точки зрения производительности эти четыре инструкции на старых процессорах 8086-80486 будут даже быстрее, чем mul в любом случае. (Pentium был, возможно, первым, кто бросил ему вызов, но опять же, с 386 вы можете использовать режим 32b и lea eax,[ebx*2+ebx+1] для выполнения 3n + 1 в одной инструкции.)

И сравните результат с 1, чтобы определить, нужно снова зациклиться.

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

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

+0

Хорошие новости и плохие новости. хорошие новости, это работает! Плохая новость заключается в том, что это как-то испортило последовательность. когда я вхожу 9, я получаю следующую последовательность: 9 28 14 3 10 5 16 8 2 0. Это неверно. правильная последовательность должна быть 9 28 14 7 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1. Я не уверен, что я забыл команду где-то в коде. тело выглядит следующим образом: –

+0

Prompt: Выходной PROMPT1 Входы String, 40 \t \t AToI Строка Mov Количество, Ax \t мов Ах, номер \t CMP топор, 0 \t JE бросить \t Itoa новый, топор \t выход нового Loop3n1: мы BX, топор \t SHR топор, 1 \t JNC wasEven \t мов ах, Ая \t добавить топор, Ье \t добавить топор, BX \t вкл топор \t Itoa новый, топор \t выход нового wasEven: MOV BX, номер \t SHR AX, 1 \t Itoa новый, топор \t выхода нового \t Mov число, ах \t cmp ax, 1 \t ja Loop3n1 \t jmp prompt –

+0

@johnerikson: Вы полностью изменили мою логику.Вам не хватает смысла, как я плотно использую промежуточные результаты от одной инструкции к другим нескольким инструкциям. Вместо того, чтобы заменять только «отображаемый топор» значением отображения, вы добавили его на два места, поэтому для каждого цикла с нечетным номером вы отображаете два значения вместо одного, и вы делаете несколько «mov bx, number», которые вы не использовали, t update и т. д. – Ped7g

Смежные вопросы