第七章 比高斯更快的計算
這一章的主題是彙編計算累加和,以及尋址方式的學習。
and、or指令
and是按位與,or是按位或。
兩者的目的操作數都必須是8位或者16位的內存單元或者通用寄存器,源操作數必須是與目的操作數同寬度的內存單元、通用寄存器或者立即數。注意,與其他指令一樣,源操作數和目的操作數不能都爲內存單元。
兩者對標誌寄存器的影響相同:OF和CF被清零,SF、ZF、PF依計算結果而定,AF位的狀態未定義。
棧
計算機中的棧段一般是從高地址向低地址生長,也就是說壓棧減地址,出棧加地址。在這裏,我們選的棧段初始地址爲0x0000,由於計算機內部採用補碼運算,所以在壓棧第一個元素時,採用0x0000-0x10,即從0xfffe開始存。由於Intel處理器採用小端方式,所以低地址存低字節,即0xfffe存低字節,0xffff存高字節,之後再壓棧、出棧都按這個邏輯。
棧段
數據段段基地址在DS中保存,附加段段基地址在ES中保存,棧段段基地址在SS中保存。
push
- 對於當前的8086平臺,push的操作數必須爲16位,也就是說,壓棧內容必須爲2字節。
- push指令執行時,首先將棧指針寄存器SP的內容減去操作數的字節長度(此處爲2),然後把要壓棧的數據存放到邏輯地址SS:SP所指向的內存位置。
- 不影響任何標誌位
pop
與push相反,過程是先取數據,再+2。同樣不影響任何標誌位。
Bochs棧調試
在Bochs中,查看棧信息使用命令“print-stack”,後面可以跟參數指定要打印接下來的多少元素,使用該命令會打印出從SP指向的棧頂向下的一系列元素。但是由於此處計算機不會區分數據區和棧區,而我們的棧高地址處是棧底元素,所以可能會打印出不在棧中的值。
8086處理器的尋址方式
1. 寄存器尋址
操作數直接位於寄存器中
2. 立即數尋址
操作數是個立即數
3. 內存尋址
3.1 直接尋址
給出一個內存地址作爲偏移地址,以此訪問
3.2 基址尋址
使用基址寄存器BX或BP來保存偏移地址。這允許在基址寄存器的基礎上使用一個偏移量,比如
mov dx, [bp-2]
3.3 變址尋址
類似於基址尋址,但是不同的是這裏使用的是變址寄存器SI和DI,同樣允許偏移量
3.4 基址變址尋址
組合BX、BP和SI、DI,來提供地址,同樣允許偏移量。
本章習題
- 把line31~37改成如下代碼即可
;以下計算1到100的和
xor ax,ax
mov cx,100
@f:
add ax,cx
loop @f
- 要求用匯編計算並顯示1+2+···+1000的結果。這個實驗有水平,很多東西我都沒想到,最後沒寫出來。下面是修改的從line30往後。
;修改計算1到1000的和
xor ax,ax
xor dx,dx
mov cx,1000
@f:
add ax,cx
adc dx,0
loop @f
;以下計算累加和的每個數位
xor cx,cx ;設置堆棧段的段基地址
mov ss,cx
mov sp,cx
mov bx,10 ;line 17~line 20還必須得有,不然不行
xor cx,cx
inc cx
div bx
or dl,0x30
push dx
@d: ;如果沒有上面那一塊,直接這一塊計算,原先dx中的高位值就會清零,結果就錯了
inc cx ;而如果上面17~20做了,由於除以了10,高位dx中就不會再有非零數據了,只保存每次計算的餘數,所以不再會出錯
xor dx,dx
div bx
or dl,0x30
push dx
cmp ax,0
jne @d
;以下顯示各個數位
@a:
pop dx
mov [es:di],dl
inc di
mov byte [es:di],0x07
inc di
loop @a
jmp near $
times 510-($-$$) db 0
db 0x55,0xaa
adc指令就是一個add指令的擴展,在計算完add之後會把CF位也加上,這樣可以使用16位寄存器計算多於16位的數據。比如這裏。**注意,因爲兩個二進制數相加,最多隻能進一位,不可能進兩位(比較容易證),所以line6~7簡單兩行代碼就能完成多於16位的計算。**因爲每次最多進一位,所以直接adc dx,0 即可保存所有高於16位的信息,不用考慮那麼多。
相比於源代碼,多出來了line17~20,原因註釋裏說了。由於計算後,dx中保存高16位,ax中保存低16位,而結果是500500,要大於16位的最大值65535,但是在一次÷10之後,50050<65535,所以在低16位,即ax中就能放得開了,dx就可以清零了,用來每次除法保存餘數。