1. 程式人生 > 實用技巧 ># 2020-10-10 #「組合語言 第 3 版 王爽」- 參考答案:課程設計 1

# 2020-10-10 #「組合語言 第 3 版 王爽」- 參考答案:課程設計 1

第一步、調整 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

最終測試結果

cd-01.JPG

參考文獻

K4NZ / 參考答案:課程設計 1
CSDN/組合語言王爽第三版答案
百度文庫/組合語言實驗答案 (王爽)