Мой ассемблерный код не запускается, и я не понимаю, почему

Код ассемблера инициализирует массивы A и B, вычисляет поэлементную сумму этих массивов и сохраняет результаты в массиве C. Он также предоставляет процедуру printarray для печати всего содержимого массива и использует эту процедуру для вывода значений массивов A, B и C на консоль. Код предполагает среду, подобную DOS, и для выполнения требуется x86-совместимая система с соответствующими инструментами сборки. но это не дало мне никакого результата, и когда я попытался отладить, программа вышла из программы

.model  small
.stack   100h
.data
A dw 12, 5, 8, -1, 4
B dw -2, 9, 0, 18, 3
C dw 5 dup (?)
N dw 5
.code
mov ax,@data
mov ds,ax
push offset A
push offset B
push offset C
push N
call arraysum
push offset A
push N
call printarray
mov ah, 2
mov dl, 10   
int 21h
push offset B
push N
call printarray
mov ah, 2
mov dl, 10   
int 21h
push offset C
push N
call printarray
mov ah, 2
mov dl, 10   
int 21h
.exit
sum proc near
push bp
mov bp,sp
mov ax,[bp+4]   
add ax,[bp+6]
pop bp
ret 4
sum endp
arraysum proc near
push bp
mov bp, sp
mov cx,[bp+4]
mov di, [bp + 10]
mov si, [bp + 8]
mov bx,[bp+6]
next:
push word ptr [di]
push word ptr [si]
call sum
pop word ptr [di]
pop word ptr [si]
mov [bx], ax
add si, 2  
add di, 2  
add bx, 2
dec cx
jnz next
pop bp
ret 8
arraysum endp

printnum proc near
    push bp
    mov bp, sp
    mov ax, [bp + 4] 
    mov bx, 10
    mov cx, 0
    
    cmp ax, 0 
    jge next
    neg ax
    
next1:   
    mov dx, 0
    div bx
    add dx, 30h
    push dx
    inc cx
    cmp ax, 0
    jne next1
    
    mov ax,[bp+4]
    cmp ax, 0
    jge sof
    push '-'
    inc cx
    
sof:
    cmp cx, 0
    jz ext
    pop dx
    mov ah, 2
    int 21h
    dec cx
    jmp sof
    
ext:
    pop bp
    ret 2   
printnum endp
printarray proc near
    push bp
    mov bp, sp
    mov cx, [bp + 4]  
    mov si, [bp + 6]  
    
print_loop:
    push word ptr [si]  
    call printnum       
    add sp, 2          
    inc si            
    dec cx            
    jnz print_loop     

    pop bp
    ret 4
printarray endp


end

jge next в printnum должно быть next1. Также printarray неверно, потому что слова занимают 2 байта, а у вас есть только inc si. Разбейте проблему на части. Сначала проверьте свой printnum, затем printarray и так далее. Исправьте свой отладчик, чтобы вы могли выполнять код самостоятельно.

Jester 27.09.2023 15:13

Вы не можете использовать ключевые слова в качестве имен переменных. C — ключевое слово. Если только ты не используешь OPTION NOKEYWORD: <C>

Nassau 27.09.2023 15:28
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
117
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вот все проблемы в коде:

  1. Проблема с кодом выхода: В коде вы используете прерывание DOS int 21h с параметром AH, установленным на 4Ch, для выхода из программы. Однако вы не установили код выхода в AL перед вызовом этого прерывания. Вам следует загрузить код выхода в AL перед вызовом этого прерывания. Например, вы можете добавить mov al, 0 перед int 21h, чтобы установить код выхода на 0.

  2. Обработка отрицательных чисел. Похоже, что код обрабатывает отрицательные числа в процедуре printnum путем добавления символа «-». Хотя это работает для отображения, фактическое число не изменяется. Если вы собираетесь работать с отрицательными числами в своей программе, вам необходимо учитывать их влияние на вычисления.

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

Код, обеспечивающий правильную работу ассемблерного кода.

Код:

.model small
.stack 100h
.data
A dw 12, 5, 8, -1, 4
B dw -2, 9, 0, 18, 3
C dw 5 dup (?)
N dw 5

.code
mov ax, @data
mov ds, ax

push offset A
push offset B
push offset C
push N
call arraysum

push offset A
push N
call printarray

mov ah, 4Ch   ; Exit program with code in AL
int 21h

sum proc near
    push bp
    mov bp, sp
    mov ax, [bp + 4]
    add ax, [bp + 6]
    pop bp
    ret 4
sum endp

arraysum proc near
    push bp
    mov bp, sp
    mov cx, [bp + 4]
    mov di, [bp + 10]
    mov si, [bp + 8]
    mov bx, [bp + 6]
next:
    push word ptr [di]
    push word ptr [si]
    call sum
    pop word ptr [di]
    pop word ptr [si]
    mov [bx], ax
    add si, 2
    add di, 2
    add bx, 2
    dec cx
    jnz next
    pop bp
    ret 8
arraysum endp

printnum proc near
    push bp
    mov bp, sp
    mov ax, [bp + 4]
    mov bx, 10
    mov cx, 0
    cmp ax, 0
    jge next
    neg ax
next1:
    mov dx, 0
    div bx
    add dl, '0'   ; Convert remainder to ASCII
    push dx
    inc cx
    cmp ax, 0
    jne next1
    mov ax, [bp + 4]
    cmp ax, 0
    jge sof
    push '-'      ; Negative sign
    inc cx
sof:
    cmp cx, 0
    jz ext
    pop dx
    mov ah, 2
    int 21h
    dec cx
    jmp sof
ext:
    pop bp
    ret 2
printnum endp

printarray proc near
    push bp
    mov bp, sp
    mov cx, [bp + 4]
    mov si, [bp + 6]
print_loop:
    push word ptr [si]
    call printnum
    add sp, 2
    add si, 2
    dec cx
    jnz print_loop
    pop bp
    ret 4
printarray endp

end

и последний вопрос, ПОЧЕМУ ВЫ ИСПОЛЬЗУЕТЕ СБОРКУ!? хе-хе просто шучу

int 20h надежно работает (на разных системах) только если CS = PSP. Если вы предлагаете _start в качестве точки входа и .model small и используете @data в качестве ссылки на сегмент, вы предполагаете сегментированный исполняемый файл MZ .EXE, который может иметь CS != PSP. Наконец, чтобы «вернуть соответствующий код выхода», вы не можете использовать int 20h, поскольку он всегда устанавливает нулевой код выхода. int 21h с AH = 4Ch полностью заменяет int 20h, при условии, что системы совместимы с MS-DOS v2+. (Кроме того, в вашем примере фактически не устанавливается AL для вызова функции 4Ch.)
ecm 27.09.2023 18:52
print_loop (ofprintarray) полагается на то, что cx сохраняется во время вызова printnum; однако printnum использует cx так, будто его можно поцарапать, и его можно поцарапать.
Erik Eidt 27.09.2023 18:56

Хорошо, я улучшу ответ. Я не эксперт в сборке.

Wise Chimpanzee 28.09.2023 03:49

Некоторые пункты вашего списка были верными; ваш ответ был лучше с этим списком исправлений, а не только с блоком кода.

Peter Cordes 28.09.2023 04:07

ок, я тоже поставлю баллы

Wise Chimpanzee 28.09.2023 04:12

спасибо, что заставил меня это понять 👍

Wise Chimpanzee 28.09.2023 04:12

Пункт 2 не имеет смысла. Ничего о div в целом и его использовании здесь не зависит от CS или CS==DS. div на ненулевое значение, при dx=0, ошибка невозможна. И делитель в bx здесь равен 10, а не 0. Даже если бы произошла ошибка, обработчик исключений загрузил бы новый CS:IP из IVT, независимо от значения CS, пока div пытался запуститься. Цикл обращается к памяти, но все эти доступы используют одну и ту же базу сегментов, SS, посредством push/pop (и перезагрузки функции arg). Не существует обращений mov cx, [cs: di], которые сделали бы CS==DS важным для работы указателей или чего-то еще. .

Peter Cordes 28.09.2023 04:46

Хорошо, я понял, спасибо за разъяснения, я думал о другом.

Wise Chimpanzee 28.09.2023 04:58
Ответ принят как подходящий

Я изменил/добавил несколько вещей.

  1. OPTION NOKEYWORD:<C>, компилятор больше не жалуется, что это ключевое слово.
  2. Линииpush offset A, B or C работают, когда директивы .186,.286,.286c или.286p применяются до .model. В других случаях программа компилируется, но не выводит результат на экран или зависает. Когда .386 стоит после .model, тоже работает. (https://stackoverflow.com/a/62603303/20889367)
  3. В sum proc строка ret 4 должна быть ret, потому что после sum proc программа извлекает значения и ret 4 меняет указатель стека на неправильное место.
  4. Порядок инструкций после звонка на sum proc должен быть pop word ptr [si], pop word ptr [di].
  5. printarray proc, значение cx уничтожается внутри printnum proc, поэтому я добавил push cx / pop cx.
  6. В printnum proc я добавил 2 байта для смещения в строках mov ax, [bp + 6] из-за приведенных выше cx инструкций и процедура заканчивается на ret, а не на ret 2.
  7. Программа завершается корректно ax = 4c00h, int 21h.
  8. Код печатает ' ' между значениями.

Результат:

12 5 8 -1 4
-2 9 0 18 3
10 14 8 17 7

Код:

OPTION NOKEYWORD:<C>

.286
.model  small
.stack   100h

.data
    A dw 12, 5, 8, -1, 4        
    B dw -2, 9, 0, 18, 3        
    C dw 5 dup (?)              
    N dw 5                      
    
.code

start:
    mov ax,@data
    mov ds,ax

;   mov dx,offset A
;   push dx
;   mov dx, offset B
;   push dx
;   mov dx,offset C
;   push dx

    push offset A
    push offset B
    push offset C
    push N

    call arraysum

;   mov dx, offset A
;   push dx

    push offset A
    push N

    call printarray

    mov ah, 2
    mov dl, 10   
    int 21h

;   mov dx, offset B
;   push dx

    push offset B
    push N

    call printarray

    mov ah, 2
    mov dl, 10   
    int 21h

;   mov dx, offset C
;   push dx

    push offset C
    push N

    call printarray

    mov ah, 2
    mov dl, 10   
    int 21h

exit:
    mov ax,4c00h
int 21h

sum proc near
    push bp
    mov bp,sp
    mov ax,[bp+4]   
    add ax,[bp+6]
    pop bp
    ret                 
sum endp

arraysum proc near
    push bp
    mov bp, sp
    mov cx,[bp+4]           ;; num rep
    mov di, [bp + 10]       ;; off array A
    mov si, [bp + 8]        ;; off array B
    mov bx, [bp+6]          ;; off array C 

next:
    push word ptr [di]      ;; push elements A[i] and B[i], i = 0 .. 4
    push word ptr [si]
    
    call sum
    
    pop word ptr [si]
    pop word ptr [di]       
                          
    mov [bx], ax
    add si, 2  
    add di, 2  
    add bx, 2
    dec cx
    jnz next
    pop bp
    ret 8           
arraysum endp

printnum proc near
    push bp
    mov bp, sp
    mov ax, [bp + 6]            
    mov bx, 10
     
    mov cx, 0                   
    
    cmp ax, 0 
    jge next1
    neg ax
    
next1:   
    mov dx, 0
    div bx
    add dx, 30h
    push dx
    inc cx
    cmp ax, 0
    jne next1
    
    mov ax,[bp + 6]
    cmp ax, 0
    jge sof
     xor ax,ax
     mov al,'-'
    push ax
    inc cx
    
sof:
    cmp cx, 0
    jz ext
    pop dx
    mov ah, 2
    int 21h
    dec cx
    jmp sof
    
ext:
    pop bp
    
    ret         ;
printnum endp

printarray proc near
    push bp
    mov bp, sp
    mov cx, [bp + 4] 
    mov si, [bp + 6] 
    
print_loop:
    push word ptr [si]  
       push cx 
    call printnum
pop cx  
    add sp, 2          
    add si,2               
    dec cx     

    mov ah, 2
    mov dl, ' '   
    int 21h
    
    jnz print_loop     

    pop bp
    ret 4
printarray endp

end start
push offset A (push imm16) действует на 186 и позже. ОП не сказал, на какую модель процессора они ориентируются, но отметил MASM. По умолчанию MASM имеет значение .model 8086 или что-то в этом роде? Если да, то лучшим решением может быть .model 386 или хотя бы .model 286, если вас не волнует совместимость с реальными ретро-машинами 8086 или EMU8086.
Peter Cordes 27.09.2023 22:32

Другие вопросы по теме