# 2020-10-10 #「組合語言 第 3 版 王爽」- 參考答案:課程設計 1
阿新 • • 發佈:2020-10-11
第一步、調整 dtoc 程式
在實驗 10 中編寫 dtoc 程式能夠顯示數值,但是存在以下問題:
1)原有 dtoc 只能支援 16 位被除數,但是收入資料是 32 位,需要新增支援;
2)除法存在溢位問題,並且原有 dtoc 程式直接使用 DIV 指令,因此可能溢位;
首先調整 dtoc 程式,使其能處理除法溢位問題,這隻需使用我們之前編寫的 divdw 函式即可。為了清晰問題,我們去掉原有註釋:
; 將數值轉化為字元換(使用函式 divdw 處理溢位) ; @desc 引數:dx => 資料高位,ax => 資料低位,ds:si => 資料寫入地址 ; @desc 結果:儲存到 ds:si 中,並以 0 結尾 dtoc: push ax push bx push cx push dx ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 計算數值字串(重點調整的部分 開始) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; mov bx, 0 push bx ; 無需理解此步驟,往下看就知道了 _do_some_stuff_start: mov dx, dx ; 呼叫 divdw 函式,高位 dx mov ax, ax ; 呼叫 divdw 函式,地位 ax mov cx, 10 ; 呼叫 divdw 函式,除數 cx call divdw ; 呼叫 divdw 函式 add cx, 30H ; 餘數加 30H 得到對應的字元 push cx ; 計算結果與顯示結果是相反的,所以先入棧,以後再彈出 mov cx, 0 ; 開始判斷商是否為零,以決定是否進入下輪 or cx, ax or cx, dx jcxz _do_some_stuff_end ; 如果 cx 為零,則表示 ax dx 都為 零,即商為零 jmp short _do_some_stuff_start _do_some_stuff_end: ; 至此,數字對應的字串都在棧中。“棧底”為零,作為 pop 邊界 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; 計算數值字串(重點調整的部分 結束) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; mov bx, 0 save_to_dateseg: pop cx jcxz _append_zero_to_sting mov ds:[bx], cl inc bx jmp short save_to_dateseg _append_zero_to_sting: mov byte ptr ds:[bx], 0 dtoc_end: pop dx pop cx pop bx pop ax ret
第二步、某些注意事項
在這個課程設計之前,我們已經編寫 show_str divdw dtoc 程式,我們會直接利用這些程式,而不會再編寫新程式。
這就帶來某些限制,比如說:
1)我們想將 單行資料寫入記憶體 之後,然後再呼叫 show_str 函式,這樣總共呼叫 21 次。但是 dtoc 程式生成的字串是包含零字元的,因此沒有辦法生成單個字串。因此我們只能寫一個列之後,就立刻呼叫 show_str 函式。
第三步、編寫 append_space 程式
根據題目要求,每個欄位(列)的寬度是固定的。因此我們需要某種函式,該函式可以追加空格來調整字串的寬度:
; 將記憶體中的字串擴充套件到指定長度 ; @desc ds:si => 字串地址,bx => 字串 最終寬度 append_space: push cx push si push bx _find_zero_start: mov ch, 0 mov cl, ds:[si] jcxz _find_zero_start_end inc si sub bx, 1 jmp short _find_zero_start _find_zero_start_end: mov cx, bx mov bl, 20H append_space_start: mov ds:[si], bl inc si loop append_space_start ; 為字串結尾 mov bl, 0 mov ds:[si], bl pop bx pop si pop cx ret
第四步、最終的程式
由於篇幅原因,我們去除 show_str divdw 等函式的註釋,只保留主體的註釋:
assume cs:codeseg datasg segment db '1975','1976','1977','1978','1979','1980','1981','1982','1983' db '1984','1985','1986','1987','1988','1989','1990','1991','1992' db '1993','1994','1995' dd 16,22,382,1356,2390,8000,16000,24486,50065,97479,140417,197514 dd 345980,590827,803530,1183000,1843000,2759000,3753000,4649000,5937000 dw 3,7,9,13,28,38,130,220H,476,778,1001,1442,2258,2793,4037,5635,8226 dw 11542,14430,15257,17800 datasg ends table segment db 160 dup(0) table ends codeseg segment start: mov dh, 1 ; 設定顯示行號 ; 21 條資料,21 次迴圈 mov cx, 21 ; 用做下標,獲取資料 mov bp, 0 mov di, 0 ; 資料寫入 table 段,ds 代表號 mov ax, table mov ds, ax mov si, 0 ; 每次迴圈都要取年份、收入、人數資料 mov ax, datasg mov es, ax ; 年份 => es:[0],收入 => es:[84],人數 => es:[168] loop_01: push cx mov cx, 2 ; 設定顯示顏色 ; mov dh, dh ; 設定顯示行號 ; 寫入年份 => 視訊記憶體 mov ax, es:0[bp] ; 開始/初始化字串 mov ds:[0], ax mov ax, es:0[bp+2] mov ds:[2], ax mov al, 0 mov ds:[4], al mov bx, 20 ; 開始/調整字串寬度 => 20 call append_space mov dl, 1 ; 開始/顯示字串 call show_str ; 寫入收入 push dx ; 開始/初始化字串 mov ax, es:84[bp] mov dx, es:84[bp+2] call dtoc pop dx mov bx, 20 ; 開始/調整字串寬度 => 20 call append_space mov dl, 21 ; 開始/顯示字串 call show_str ; 寫入人數 push dx ; 開始/初始化字串 mov dx, 0 mov ax, es:168[di] call dtoc pop dx mov bx, 20 ; 開始/調整字串寬度 => 20 call append_space mov dl, 41 ; 開始/顯示字串 call show_str ; 寫入平均收入 push dx ; 開始/初始化字串 push cx mov dx, es:84[bp+2] mov ax, es:84[bp] mov cx, es:168[di] call divdw ; dx, ax...cx call dtoc pop cx pop dx mov bx, 20 ; 開始/調整字串寬度 => 20 call append_space mov dl, 61 ; 開始/顯示字串 call show_str ; 調整變數,進入下一輪迴圈 add bp, 4 ; 取值的索引需要調整 add di, 2 inc dh ; 行號需要調整 ; 進入下一輪迴圈 pop cx loop loop_01 mov ax, 4c00h int 21h ; 將記憶體中的字串擴充套件到指定長度 ; @desc ds:si => 字串地址,bx => 字串 最終寬度 append_space: push cx push si push bx _find_zero_start: mov ch, 0 mov cl, ds:[si] jcxz _find_zero_start_end inc si sub bx, 1 jmp short _find_zero_start _find_zero_start_end: mov cx, bx mov bl, 20H append_space_start: mov ds:[si], bl inc si loop append_space_start ; 為字串結尾 mov bl, 0 mov ds:[si], bl pop bx pop si pop cx ret ; 將數值轉化為字元換(使用函式 divdw 處理溢位) ; @desc 引數:dx => 資料高位,ax => 資料低位,ds:si => 資料寫入地址 ; @desc 結果:儲存到 ds:si 中,並以 0 結尾 dtoc: push ax push bx push cx push dx mov bx, 0 push bx _do_some_stuff_start: mov dx, dx mov ax, ax mov cx, 10 call divdw add cx, 30H push cx mov cx, 0 or cx, ax or cx, dx jcxz _do_some_stuff_end jmp short _do_some_stuff_start _do_some_stuff_end: mov bx, 0 save_to_dateseg: pop cx jcxz _append_zero_to_sting mov ds:[bx], cl inc bx jmp short save_to_dateseg _append_zero_to_sting: mov byte ptr ds:[bx], 0 dtoc_end: pop dx pop cx pop bx pop ax ret ; 顯示字串 ; @desc 引數:dh => 行,dl => 列,cl => 顏色,ds:si => 字串地址,以 0 結尾 show_str: _show_str_start: push ax push bx push dx push cx push es push si mov ax, 0B800H mov es, ax sub dh, 1 mov al, dh mov bl, 0A0H mul bl ; ax sub dl, 1 add dl, dl mov dh, 0 ; dx add dx, ax mov bx, dx mov ah, cl ; 顏色 _copy_char: mov cl, ds:[si] mov ch, 0 jcxz _show_str_end mov al, cl mov es:[bx], ax inc si add bx, 2 jmp short _copy_char _show_str_end: pop si pop es pop cx pop dx pop bx pop ax ret ; 解決除法溢位問題 ; @desc 引數:dx => 被除數高位,ax => 被除數低位,cx => 除數 ; @desc 結果:dx => 結果高位,ax => 結果低位,cx => 餘數 divdw: push bx push ax mov ax, dx mov dx, 0 div cx mov bx, ax pop ax div cx mov cx, dx mov dx, bx pop bx ret codeseg ends end start