Professional Documents
Culture Documents
• Phần trên cùng hiển thị nội dung của 32 thanh ghi của CPU và FPU. Nội dung sẽ
tự động cập nhật khi chương trình hợp ngữ chạy.
• Phần dưới kế tiếp hiển thị mã của chương trình ở dạng hợp ngữ, dạng mã máy (số
hex cột thứ 2 từ trái qua), và địa chỉ tương ứng của mỗi lệnh (cột đầu tiên bên trái).
• Phần dưới kế tiếp hiển thị dữ liệu khai báo trong chương trình hợp ngữ (ví dụ:
mảng hay chuỗi) và dữ liệu trong vùng ngăn xếp khi chương trình hợp ngữ được
thực thi.
• Phần dưới cùng hiển thị các thông tin phụ của SPIM, thông tin về lỗi nếu có.
Chương trình hợp ngữ muốn chạy được phải được load trước. Đế load chương trình hợp
ngữ (ở dạng một file có đuôi mở rộng là *.asm hay *.s), thực hiện thao tác File menu ->
Open -> chọn file chứa chương trình cần load. Để tạo file *.asm, chúng ta có thể dùng các
chương trình soạn thảo thô như Notepad, Notepad++, EditPlus…
Hình 2. Load file chứa chương trình nguồn.
Ví dụ:
Số thập lục phân 0xeca86420 chuyển sang số nhị phân:
Số nhị phân 1 0011 0101 0111 1001 1011 1101 1111 chuyển sang số thập lục phân:
• load: nhóm byte liên tục nhau theo chiều tăng địa chỉ ô nhớ bắt đầu từ địa chỉ
được chỉ định được copy vào thanh ghi.
• store: nhóm dữ liệu 1, 2 byte thấp của thanh ghi hay cả 4 byte của thanh ghi được
copy vào bộ nhớ từ địa chỉ được chỉ định.
MIPS có tấc cả 32 thanh ghi 32-bit có thê sử dụng trong ngôn ngữ assembly được liệt kê
trong bảng sau:
Bộ xử lý MIPS có bộ tính toán số học/luận lý (ALU) bên trong thực hiện các phép toán
số học và luận lý trên số nguyên 32-bit. Phép toán thực
hiện bởi ALU gồm hai toán hạng. Một toán hạng là số
nguyên chứa trong thanh ghi, toán hạng còn lại có thể
chứa trên thanh ghi hay là một phần của lệnh thực hiện
phép toán (hằng số). Kết quả của phép toán luôn đặt vào
thanh ghi.
3. Tập lệnh của MIPS
Lệnh Cú pháp Định dạng lệnh Ý nghĩa
AND từng bit với andi rd, rs, const rd Å bitwise AND of rs with
hằng số 16 bit const
Nhảy nếu bằng beq rs, rt, addr nhảy đến addr nếu rs == rt. Cần
phải thêm delay theo sau (nop)
Nhảy nếu lớn hơn bgez rs, addr nhảy đến addr nếu số bù 2 trong
hay bằng 0 rs >= 0. Cần thêm delay
Nhảy nếu nhỏ hơn bltz rs, addr nhảy đến addr nếu số bù 2 trong
0 rs < 0. Cần thêm delay
Nhảy nếu khác bne rs, rt, addr nhảy đến addr nếu rs != rt. Cần
thêm delay
Load hằng vào 16 lui rd, const 2 byte cao của rd Å 16 bit const
bit cao 2 byte thấp của rd Å 0x0000
NOT từng bit nor rd, rs, $0 rd Å NOT từng bit của rs
NOR từng bit nor rd, rs, rt rd Å NOR từng bit của rs và rt
Lưu byte thấp của sb rs, offset(base) byte ở offset +base Å byte thấp
thanh ghi vào bộ của rs, offset dạng bù 2
nhớ
Lưu hai byte thấp sh rs, offset(base) 2 byte ở offset +base Å 2 byte
vào bộ nhớ thấp của rs, offset dạng bù 2
Lệnh nop sll $0, $0, 0 tạo thời gian trễ cần thiết
Dịch trái không sll rd, rs, shft rd Å rs sau khi dich trái shft
dấu bit; 0 <= shft <32
Dịch phải có dấu sra rd, rs, shft rd Å rs sau khi dich phải shft
bit và mở rộng dấu;
0 <= shft <32
Dịch phải luận lý srl rd, rs, shft rd Å rs sau khi dich phải shft
bit; 0 <= shft <32
Trừ hai thanh ghi sub rd, rs, rt rd Å rs – rt; các toán hạng dạng
dạng bù 2 bù 2
Trừ hai thanh ghi subu rd, rs, rt rd Å rs – rt; các toán hạng dạng
dạng không dấu không dấu
Lưu thanh ghi vào sw rs, offset(base) word ở địa chỉ offset + base Å
bộ nhớ $rs; offset dạng bù 2
XOR từng bit với xori rd, rs, rt rd Å XOR từng bit rs và hằng
hằng sau khi mở rộng 0
4. Cú pháp của MIPS Assempler
Chú thích (comment) chuỗi các từ bắt đầu bằng #, tất cả các từ bắt đầu từ # cho đến cuối
dòng đều được bỏ qua.
Tên định danh (identifier) là chuỗi các ký tự chữ, số, dấu gạch dưới (_) và dấu chấm (.)
không bắt đầu bằng số. Tên không được trùng với các từ dành riêng là opcode của lệnh.
Các ví dụ về tên định danh hợp lệ:
main, loop, end_if, case1.2
Các ví dụ về tên định danh không hợp lệ:
1value # số đứng đầu
b # trùng với opcode lệnh nhảy
add # trùng với opcode lệnh cộng
Nhãn bao gồm tên định danh theo sau là dấu hai chấm (:) được đặt ở đầu dòng.
Ví dụ:
.data
item: .word 1
.text
.globl main # must be global
main:
…
Số (number) mặc định là cơ số 10. Số thập lục phân (hexadecimal) thêm 0x vào phía
trước. Hai số 256 và 0x100 diễn tả số có cùng giá trị.
Chuỗi (string) được đặt giữa hai dấu nháy kép (“). Ví dụ: “Hello world!\n”. Các ký tự
đặt biệt cho phép trong chuỗi:
newline \n
tab \t
quote \”
Chỉ thị (directive) được hỗ trợ gồm có:
.text
.globl main
main:
addiu $8, $0, 1 # put x into $8
mult $8, $8 # lo = x**2
mflo $9 # $9 = x**2
ori $7, $0, 3 # $7 = 3
ori $6, $0, 5 # $6 = 5
mult $7, $9 # lo = 3x**2
mflo $9 # $9 = 3x**2
ori $7, $0, 5 # $7 = 5
addi $9, $9, -8 # $9 = 3x**2 - 8
mult $7, $8 # lo = 5x
mflo $7 # $7 = 5x
addu $9, $9, $7 # $9 = 3x**2 + 5x - 8
## End of file
.text
.globl main
main:
lui $9,0x1234 # put data in $9
ori $9,0x5678 #
lui $8,0x1000 # $8 is base register
sb $9,3($8) # least significant byte
srl $9,$9,8 # move next byte to low order
sb $9,2($8) # bits 8-15
srl $9,$9,8 # move next byte to low order
sb $9,1($8) # bits 16-23
srl $9,$9,8 # move next byte to low order
sb $9,0($8) # most significant byte
.data
tape: # base register points here
.space 1024 # tape buffer (1K bytes)
## End of file
.text
.globl main
main:
ori $8,$0,0x0029 # load $8 with the pattern
sll $8,$8,16 # shift into MSBs
ori $8,$8,0x8D7D # or in the LSBs
loop: beq $8,$0,exit # done when pattern == 00
sll $0,$0,0 # delay or nop after a branch
# or jump instruction
srl $8,$8,1 # shift right one bit
addu $7,$7,1 # increment shift count
j loop # repeat
sll $0,$0,0
## End of file
## Register Use:
##
## $8 --- current character
## $10 --- character pointer
.text
.globl main
.data
string: .asciiz "ABCDEFGHIJKLMNOP"
## End of file
div d,s,t # divide $s by $t. Put the quotient in $d. Operands are
# two's complement.
divu d,s,t # divide $s by $t. Put the quotient in $d. Operands are
# unsigned.
remu d,s,t # divide $s by $t. Put the remainder in $d. Operands are
# unsigned.
.globl main
# Calculations
lw $s1,tip # get tip rate
lw $s2,tax # get tax rate
addu $s3,$s1,$s2 # (tax + tip) in percent
mul $s4,$s0,$s3 # mealcost*(total rate)
div $s4,$s4,100 # mealcost*(total rate)/100
addu $s5,$s0,$s4 # total bill
# Output
li $v0,4 # print string
la $a0,head1 # "tax plus tip"
syscall
li $v0,10 # exit
syscall
.data
tip: .word 15 # tip rate in percent
tax: .word 8 # tax rate in percent
exit 10
Sau đây là ví dụ sử dụng hàm hệ thống để in chuỗi ký tự và kết thúc chương trình:
# hello.asm
#
.text
.globl main
main:
li $v0,4 # code 4 == print string
la $a0,string # $a0 == address of the string
syscall # Invoke the exception handler.
.data
string: .asciiz "Hello SPIM!\n"
# end of file
.text
.globl main
main:
# get patron name
li $v0,4 # print prompt
la $a0,prompt #
syscall
li $v0,8 # code 8 == read string
la $a0,name # $a0 == address of buffer
li $a1,24 # $a1 == buffer length
syscall # Invoke the operating system.
li $v0,10 # exit
syscall
.data
prompt: .asciiz "enter name, followed by comma-enter: "
letter: .ascii "\n\nDear "
name: .space 24
# end of file
.globl main
main:
lw $t0,a # get a
lw $t1,bb # get b
mul $t0,$t0,$t1 # a*b
subu $sp,$sp,4 # push a*b onto stack
sw $t0,($sp)
lw $t0,a # get a
li $t1,-12 #
mul $t0,$t0,$t1 # -12a
subu $sp,$sp,4 # push -12a onto stack
sw $t0,($sp)
lw $t0,bb # get b
li $t1,18 #
mul $t0,$t0,$t1 # 18b
subu $sp,$sp,4 # push 18b onto stack
sw $t0,($sp)
li $t1,-7 # init sum to -7
lw $t0,($sp) # pop 18b
addu $sp,$sp,4
addu $t1,$t1,$t0 # 18b -7
lw $t0,($sp) # pop ab
addu $sp,$sp,4
addu $t1,$t1,$t0 # ab - 12a + 18b -7
.data
a: .word 0
bb: .word 10
.text
.globl main
main:
jal pread # read first integer
nop # branch delay slot
move $s0,$v0 # save it in $s0
jal pread # read second integer
nop # branch delay slot
move $s1,$v0 # save it in $s1
jal pread # read third integer
nop # branch delay slot
move $s2,$v0 # save it in $s2
li $v0,10 # exit
syscall
jr $ra # return
nop # branch delay slot
.data
prompt:
.asciiz "Enter an integer: "
5.10 Gọi hàm dùng stack
Các chú ý khi một thủ tục gọi một thủ tục con dùng stack:
Gọi thủ tục con (thực hiện bởi chương trình gọi):
1. Push vào stack các thanh ghi $t0-$t9 cần lưu giá trị. Thủ tục con có thể thay đổi các
thanh ghi này.
2. Gán giá trị vào các tham số của thủ tục con $a0-$a3.
3. Gọi thủ tục con sử dụng jal.
4. Nếu thủ tục con này có thể gọi các thủ tục con khác, phải push $ra vào stack.
5. Push vào stack các thanh ghi $s0-$s7 nếu thủ tục con này có thể thay đổi chúng.
6. Thủ tục con có thể thay đổi các thanh ghi T, A hoặc S (nếu như S đã được lưu ở bước 5).
7. Nếu thủ tục con này gọi thủ tục con khác thì nó phải tuân thủ các chú ý này.
Phần cuối thủ tục con (Thực hiện trước khi trở về chương trình gọi):
Phục hồi điều khiển sau khi trở về từ chương trình con (thực hiện bởi chương trình gọi):
12. Pop ra khỏi stack (theo thứ tự ngươc) các thanh ghi $t0-$t9 đã được push vào stack
ở bước 1.
.text
.globl main
main:
sub $sp,$sp,4 # push the return address
sw $ra,($sp)
sub $sp,$sp,4 # push $s0
sw $s0,($sp)
# prepare arguments
move $a0,$s0 # x
move $a1,$v0 # y
jal maxExp # maximum expression
nop # returned in $v0
move $s0,$v0 # keep it safe
jr $ra # return to OS
nop
.data
xprompt: .asciiz "Enter a value for x --> "
yprompt: .asciiz "Enter a value for y --> "
rprompt: .asciiz "The maximum expression is: "
## maxInt -- compute the maximum of two integer arguments
##
## Input:
## $a0 -- a signed integer
## $a1 -- a signed integer
##
## Returns:
## $v0 -- maximum
.text
.globl maxInt
maxInt:
# body
move $v0,$a0 # max = $a0
bgt $a0,$a1,endif # if $a1 > $a0
nop
move $v0,$a1 # max = $a1
endif: # endif
# epilog
jr $ra # return to caller
nop
## maxExp -- compute the maximum of three expressions
##
## Input:
## $a0 -- a signed integer, x
## $a1 -- a signed integer, y
##
## Returns:
## $v0 -- the maximum of x*x, x*y, or 5*y
##
## Registers:
## $s0 -- x*x
## $s1 -- x*y
## $s2 -- 5*y
.text
.globl maxInt
maxExp:
# prolog
sub $sp,$sp,4 # push the return address
sw $ra,($sp)
sub $sp,$sp,4 # push $s0
sw $s0,($sp)
sub $sp,$sp,4 # push $s1
sw $s1,($sp)
sub $sp,$sp,4 # push $s2
sw $s2,($sp)
# body
mul $s0,$a0,$a0 # x*x
mul $s1,$a0,$a1 # x*y
li $t0,5
mul $s2,$t0,$a1 # 5*y
Các chú ý khi một thủ tục gọi một thủ tục con dùng stack pointer và frame pointer:
Gọi thủ tục con (thực hiện bởi chương trình gọi):
1. Push vào stack các thanh ghi $t0-$t9 cần lưu giá trị. Thủ tục con có thể thay đổi các thanh
ghi này.
2. Gán giá trị vào các tham số của thủ tục con $a0-$a3.
3. Gọi thủ tục con sử dụng jal.
9. Thủ tục con có thể thay đổi các thanh ghi T, A hoặc S (nếu như S đã được lưu ở bước 5).
10. Thủ tục con tham khảo đến biến cục bộ sử dụng offset($fp)
11. Thủ tục con tự do push, pop phần tử vào stack
12. Nếu thủ tục con này gọi thủ tục con khác thì nó phải tuân thủ các chú ý này.
Phần cuối thủ tục con (Thực hiện trước khi trở về chương trình gọi):
Phục hồi điều khiển sau khi trở về từ chương trình con (thực hiện bởi chương trình gọi):
19. Pop ra khỏi stack (theo thứ tự ngươc) các thanh ghi $t0-$t9 đã được push vào stack ở
bước 1.
# main()
# {
# int a, b; // a: 0($fp), b: 4($fp)
# write("enter an int:")
# read( a );
# b = fact( a );
# write("factorial is:")
# print( b );
# }
.text
.globl main
main:
addiu $sp,$sp,-4
sw $ra,0($sp) # 1. Push return address
addiu $sp,$sp,-4
sw $fp,0($sp) # 2. Push caller's frame pointer
# 3. No S registers to push
addiu $fp,$sp,-8 # 4. $fp = $sp - space_for_variables
addu $sp,$fp,$0 # 5. $sp = $fp
li $v0,5 # read( a )
syscall
# subroutine call
# 1. No T registers to push
addu $a0,$v0,$0 # 2. Put argument into $a0
jal fact # 3. Jump and link to subroutine
nop
# return from subroutine
# 1. No T registers to restore
sw $v0,4($fp) # b = fact( a )
li $v0,4 # write("factorial is:")
la $a0,prompt2
syscall
lw $a0,4($fp) # print( b )
li $v0,1
syscall
# epilog
# 1. No return value
addu $sp,$sp,8 # 2. $sp = $fp + space_for_variables
# 3. No S registers to pop
lw $fp,0($sp) # 4. Pop $fp
lw $ra,4($sp) # 5. Pop $ra
addu $sp,$sp,8
jr $ra # return to OS
nop
.data
prompt1: .asciiz "enter an int:"
prompt2: .asciiz "factorial is:"
#addi $t0,$0,2
slti $t0,$a0,2 # test for n < 2
beq $t0,$0,else
else:
addi $a0,$a0,-1 # else decrement n
jal fact # recursive call
nop
lw $a0,4($sp) # restore original n
lw $ra,0($sp) # and return address
addi $sp,$sp,8 # pop 2 items from stack