2017-08-01 278 views
1

我遇到了一個問題,在程序集8086中只有一行顯示4個不同的字符串。輸出應該是「你是」,「名字」,「中間名」和「姓氏」。它與前兩項工作正常,但最後兩個重疊與第一個,意思是,「你」最終被重寫「中間名」,並進一步被「姓氏」重寫。如果在最後兩行之前使用下一行,它會打印出正常,但我想在一行中顯示所有4個字符串,而不是以3行顯示。我試圖搜索網絡,但大多數答案僅限於顯示2個字符串。爲什麼多個字符串在彙編中的輸出中重疊/覆蓋?

;=====output====== 

mov ah, 09 
mov dx, offset crlf ;next line 
int 21h 

mov ah, 09 
mov dx, offset msg4    ;displays "You are" 
int 21h 

mov ah, 09 
mov dx, offset string1 + 2  ;displays inputted "first name" 
int 21h 

mov ah, 09 
mov dx, offset string3 + 2  ; this should appear next to string1, 
int 21h       not rewrite msg4... 

mov ah, 09 
mov dx, offset string2 + 2  ; this should appear next to string3, not 
int 21h        rewrite msg4 and string3 

這就是輸出端像:

Enter 1st name: Helena 
Enter last name: Ramos 
Enter middle name: Ang 

Ramosre Helena     ;"Ang" rewrites "You are", and then 
            "Ramos" rewrites it again 

           ; This is what I want to see: 
           ;  You are Helena Ang Ramos 

我非常組裝一個新手,我的教授是不完全的最有幫助的老師,因爲我們不」沒有任何書籍,課堂講義只定義說明,而實驗練習幾乎是複製粘貼他編寫的代碼,所以我的大多數同學都是在實際編程中自學。這只是作業的一小部分,實際的作業要求我們顯示中間名,而不是中間名,但我甚至無法正確顯示所有4個字符串!在這一點上,我有一種感覺,如何將字符串推入堆棧有一個問題,但是我有限的知識阻止了我找出原因。如果你有興趣

全碼:

org 100h 
.model small 
.stack 200 

.data 
msg1 db "Enter 1st name: $" 
string1 db 50,?,50 dup ('$') 
msg2 db 0ah, 0dh, "Enter last name: $" 
string2 db 50,?,50 dup ('$') 
msg3 db 0ah, 0dh, "Enter middle name: $" 
string3 db 50,?,50 dup ('$') 

msg4 db 0ah, 0dh, "You are $" 

crlf db 0ah, 0dh, '$' 

.code 

mov ax, @data 
mov ds, ax 

mov ah, 09 
mov dx, offset msg1 
int 21h 

mov ah, 0ah 
mov dx, offset string1 ;input first name 
int 21h 

mov ah, 09 
mov dx, offset msg2 
int 21h 

mov ah, 0ah 
mov dx, offset string2 ;input last name 
int 21h 

mov ah, 09 
mov dx, offset msg3 
int 21h 

mov ah, 0ah 
mov dx, offset string3 ;input middle name 
int 21h 

;=====output====== 

mov ah, 09 
mov dx, offset crlf ;next line 
int 21h 

mov ah, 09 
mov dx, offset msg4    ;displays "You are" 
int 21h 

mov ah, 09 
mov dx, offset string1 + 2  ;displays inputted "first name" 
int 21h 

mov ah, 09 
mov dx, offset string3 + 2  ; this should appear next to string1, 
int 21h       not rewrite msg4... 

mov ah, 09 
mov dx, offset string2 + 2  ; this should appear next to string3, not 
int 21h        rewrite msg4 and string3 

回答

2

您應該檢查調試器,會發生什麼。

如果你會,你會看到,在第一個提示符下輸入「海倫娜」後,在string1地址存儲器內容:

32 07 48 65 6C 65 6E 61 0D 24 24 24 24 ... 

這些數據最重要的是價值0D也稱爲CR(回車)在地址string1 + 2 + 7 - 1(+2獲取字符串數據,+7是輸入的長度,-1回到最後一個字符)。

同樣的事情適用於其他兩個輸入。

一旦你將開始輸出線下決賽,你會寫在屏幕上:

You are Helena有來自第一CR,這將返回BIOS光標回線的開始,但後面沒有LF(0Ah),所以光標不會向下移動一行,而是第二個輸入的輸出將剛好覆蓋該行的開頭。

要解決:顯示用戶輸入的每個名字的前面,這樣做:

mov ah, 09 
    mov dx, offset string1 + 2  ;displays inputted "first name" 

    movzx bx, byte ptr [dx-1]  ; bx = length of input 
    mov byte ptr [bx + dx - 1], '$' ; overwrite last input char (most likely CR) with '$' 
    int 21h 

(如果實模式不支持尋址模式[bx + dx -1],只是做add bx,dx和使用[bx-1]然後...我內存朦朧,我正在做更多的保護模式x86彙編,其中尋址模式更加寬鬆和普遍)。

而且,如果8086沒有甚至movzx,然後xor bx,bxmov bl,[dx-1]可以做同樣的事情(讀書字節從內存中,其零擴展到字)。

編輯:實際上,你可能要覆蓋最後一個字符,而與SPACE ' ',把名之間的空間......而在string3你可能會mov word ptr [bx+dx-1],0A0Dh覆蓋它在它的末端CR + LF對新寫行,但是您應該在string3緩衝區之後再放一個字節,以避免用最長可能的輸入(全部50個字符)進行內存覆蓋。


編輯:一些更多評論...

沒有任何書籍

這是互聯網時代。教你8086(可能是我猜的emu8086)本身就是一種殘酷的笑話,然後這個知識再次給你關於未來任何相關編程的新視角,所以即使是8086教學也是值得的。你應該能夠谷歌一些emu8086教程(雖然我不確定質量,由一些問題來判斷,所以很難找到僅包含基本知識的教程,但是正確...然後是"The Art of Assembly Language Programming"書,它是免費的閱讀它的電子表格供個人使用,它非常全面詳細......但也非常巨大(如果你的課程真的很糟糕,你可能仍然希望檢查16b DOS版本,並且很少有章節快速查找查明需要在你身邊一個適當的研究領域)。

我有一種感覺,有一個與琴絃是如何在堆棧

你做推的一個問題不要在代碼中的任何地方觸摸堆棧......在這種情況下,您的評論聽起來非常可怕,您應該深入研究該書,即使這意味着要讀取200-300頁。畢竟,你並沒有使用「緊急」,所以你可能有幾個月的時間來跟上程序集和計算機體系結構的基礎知識。


「SPACE編輯」的FIX:

但是,當您覆蓋與空間最後一個字符,然後如果用戶輸入50個字符長的名字,這個名字將不會有更多'$'終止,所以你應該而定義的緩衝區有固定的終止超越他們,就像這樣:

string1 db 50,0,51 dup ('$') ; after 50 byte buffer there's one more '$' 

這是ASM編程中最難的部分之一,以避免任何緩衝/堆棧溢出錯誤,從錯誤的數據定義詞幹和不安全使用的記憶。在調試器中始終使用最小/最大輸入測試代碼,並觀察內存內容以查看它是否按預期運行,或者是否發生了意外的內存覆蓋以及在何處發生。您可能還需要定義一些警戒值緩衝區之間,這樣的:如果你在調試器中看到的一些角落情況下的輸入並取得了FF字節不見了

string1 db 50,0,51 dup ('$') 
    db 0FFh 
msg2 db 0ah, 0dh, "Enter last name: $" 

然後,你知道你有你的源錯誤(例如,如果第一個字節爲52,用戶輸入52個字符長名稱)。


修復與實模式有效的8086碼(原提示尋址模式[dx]這是不合法的實模式):

添加第一個程序的代碼的結束,這將覆蓋最後輸入字符+(長度存儲在內存中的領先字符串本身的字節)的類Pascal串多一個字節:

; input: 
; dx = address of string ([dx-1] must contain length of string) 
; ax = two chars to be written at the end of string (al = first, ah = second) 
changeEndOfInputString: 
    push si   ; preserve original si and bx values 
    push bx 
    mov si,dx  ; use SI for addressing in real mode 
    xor bx,bx  ; bx = 0 
    mov bl,[si-1] ; bx = (zero extended) string length 
    mov [si+bx-1],ax ; overwrite last inputted char + one more 
    pop bx   ; restore bx and si and return 
    pop si 
    ret 

現在名稱字符串的顯示將用它來增加空間和新的生產線,其中需要。

;=====output====== 

    ;display "You are " 
    mov ah, 9 
    mov dx, offset msg4 
    int 21h 

    ;display inputted "first name" with space added 
    mov dx, offset string1 + 2 
    mov ax, 2420h ; 20h = ' ', 24h = '$' (ASCII encoding) 
    call changeEndOfInputString 
    mov ah, 9 
    int 21h 

    ;display inputted "middle name" with space added 
    mov dx, offset string3 + 2 
    mov ax, 2420h ; 20h = ' ', 24h = '$' (ASCII encoding) 
    call changeEndOfInputString 
    mov ah, 9 
    int 21h 

    ;display inputted "last name" with CR+LF added 
    mov dx, offset string2 + 2 
    mov ax, 0A0Dh ; 0Dh = CR, 0Ah = LF (DOS "new line") 
    call changeEndOfInputString 
    mov ah, 9 
    int 21h 

,並確保您的字符串緩衝區必須在年底額外字節數這些修改,以適應:

string1 db 50,0,51 dup ('$') ; first name buffer 
string2 db 50,0,52 dup ('$') ; last name buffer 
string3 db 50,0,51 dup ('$') ; middle name buffer 

的「姓」緩衝區需要52「$」,因爲如果用戶輸入整整50個字符名稱,第50和第51個字符將被覆蓋到CR + LF,因此第52個'$'將爲您節省作爲int 21h,9服務的字符串終止符。

由於輸入將被修改爲' '+'$',所以第一個+中間名緩衝區與51'$'一致,所以在輸入50個字符的情況下,第51個字符即使在修改後也會保持設置爲'$'。

0Ah服務緩衝區第二個字節也設置爲0,不?,因爲它是在一些DOS版本的實際輸入值,告訴DOS怎樣的緩衝區內容多少是有效的輸入編輯,並且不提供任何有效的在緩衝區內調用之前的字符串。

而且最後要注意的

crlf db 0ah, 0dh, '$' 

這是LF + CR(順序錯誤),在DOS 「新行」 應該是CR + LF,即13,10,10,13大多按預期工作,但在彙編世界,這是任何方式的錯誤,你只是幸運的,它沒有更大的影響。 (你在所有字符串定義中都有錯)。

所以我試着用bx代替它,並且沒有錯誤,但它仍然覆蓋。

你絕對MUST學習使用調試器,讓你有一些機會,以檢查其中的代碼不會做你期待什麼,什麼是存儲器和寄存器在這種情況下的內容。

你就沒有機會在組件程序而不調試器,花了 3讀取你原來的問題,徹底慢(約10分鐘)模擬在我的頭(懶得搜索8086仿真器和彙編程序兼容你的語法),直到我找出你爲什麼要覆蓋字符串,因爲「唯一的CR」不容易從源頭髮現。我做了幾年的x86彙編編程,寫了幾兆字節的ASM源代碼。發現這個錯誤仍然很棘手。如果我有調試器並通過它運行,我會在用戶輸入後檢查緩衝區內容string1後立即發現問題。或者用不同的例子來解釋調試器的需求,當我學習ASM編程時,我沒有電腦,所以我不得不在書面上編寫代碼,然後我在學校有幾分鐘的時間試用它通常每週一次。通常它由於一些錯誤而崩潰,但我沒有足夠的時間在機器上進行調試,所以我必須在論文中找到家中的錯誤(並且在1985年左右沒有因特網返回來詢問SO)。我經常花3-5周的時間來修復所有錯誤的工作版本。如果我隨意使用計算機,使用調試器,我會在1小時內完成所有修復。再次,現在我通過閱讀源代碼(甚至在其他編程語言中)看到了很多錯誤,這就是我的大腦經歷了紙張體驗後,注意到每個點,昏迷和編號的過程中的許多錯誤...

+0

我使用8086,所以movzx不起作用。我試過'mov bl,[dx-1]',但是它會導致一個錯誤,說'(63)十六進制可能沒有零前綴;或沒有'h'後綴;或錯誤的尋址;或undefined var:[dx-01h]'我嘗試過使用'sub [dx],01'或'dec [dx]'來解決它,但它會導致相同的錯誤。間接尋址不接受dx,所以我嘗試用bx替換它,並且沒有錯誤,但仍然覆蓋。我被dx卡住了。 – AnAn

+0

@安娜哦,真正的模式的喜悅,我完全忘了那些。我將在幾分鐘內(在得到一些彙編器來驗證代碼之後)用有效的實模式尋址和8086指令編輯答案。順便說一句'sub [dx],1'會從內存中減去1,而不是'dx'。括號'[]'的意思是「使用內部括號中的值作爲內存中的地址以在那裏操作數值」。 – Ped7g

+0

@AnAn在答案的最後......基本上是新的答案......但是你需要更加努力地嘗試自己,我並不是指隨機猜測並嘗試從其他地方複製代碼副本。這可能適用於高級語言,但在彙編中,您必須完全理解您寫的內容,在合理的時間內「猜出」正確的代碼幾乎是零。因此,根據您當前的問題(「堆棧」提及,顛倒LF + CR,無法使用錯誤的[[dx]'尋址修復我的原始代碼,無法使用調試器),您需要閱讀某本書/教程,花幾周時間它,並試驗代碼。 – Ped7g

相關問題