Tải bản đầy đủ (.pdf) (26 trang)

Giới thiệu PCSPim pot

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (580.8 KB, 26 trang )

1. Giới thiệu PCSPim
PCSpim là chương trình chạy và mô phỏng chương trình hợp ngữ dành cho MIPS. Sau
khi cài đặt PCSpim và chạy chương trình (Start -> All Programs -> PCSpim.exe), cửa sổ
chương trình PCSpim hiện lên như hình 1.

Hình 1: Cửa sổ chương trình PCSpim
Cửa sổ hiển thị chia làm 4 phần:
• 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.

Sau khi chương trình hợp ngữ đã được load, chúng ta
có thể thực hiện chạy chương trình và quan sát sự thay
đối giá trị các thanh ghi, các ô nhớ, vị trí và lệnh đang
được thực thi … Các tác vụ chạy chương trình ở trong
Simulator menu.
Các bước để chạy và quan sát quá trình chạy của
chương trình hợp ngữ trên PCSpim:
• Chọn Simulator -> Breakpoints… (hoặc nhấn


Ctrl+B). Cửa sổ hiện ra điền vào textbox
Address giá trị 0x00400000, chọn Add.
• Chọn Simulator -> Go (hoặc nhấn F5).
Điền
giá trị 0x00400000 vào texbox Starting
Address, chọn OK. Giá trị của thanh ghi PC
lúc này là 0x00400000.
• Chọn Simulator -> Single Step (hoặc nhấn F10) để chạy từng dòng lện trong
chương trình. Chúng ta có thể quan sát kết quả thực hiện thông qua giá trị của các
thanh ghi, các ô nhớ liên quan.
Cách khác để chạy chương trình nguồn: Simularor -> SetValue…, cửa sổ hiện ra, điền
vào textbox Register Name giá trị PC, điền vào textbox Value giá trị 0x00400000. Sau đó
nhấn F10 để chạy từng bước hay F5 để chạy cho đến khi gặp breakpoints.
2. Các kiến thức cơ sở
2.1 Đổi qua lại giữ số thập lục phân (hexadecimal) và số nhị phân (binary)
Số thập lục phân được tạo thành từ 16 ký số: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f. Số
nhị phân tạo thành từ 2 ký số: 0, 1. Một ký số thập lục phân tương ứng với số nhị phân gồm 4
ký số theo bảng sau:

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:

2.2 Tổ chức bộ nhớ
Bộ nhớ là một mảng 2
32
ô nhớ
8-bit, bắt đầu từ địa chỉ 0x000000
đến địa chỉ 0xFFFFFFFF. Người

dùng chỉ có thể sử dụng vùng nhớ
từ 0x00400000 đến 0x7FFFFFFF.
Vùng nhớ người dùng chia làm 3
phần: vùng chứa mã (text segment),
vùng chứa dữ liệu (data segment)
và vùng nhớ stack (stack segment). Bộ xử lý tương tác với bộ nhớ thông qua việc di chuyển
dữ liệu giữa các ô nhớ và các thanh ghi. Có hai thao tác để thực hiện việc di chuyển dữ liệu
giữa bộ nhớ và thanh ghi: load và store. Dữ liệu di chuyển được tổ chức thành nhóm một,
hai hay bốn byte liên tục nhau theo chiều tăng địa chỉ ô nhớ.
• 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.
2.3
Tổ chức thanh ghi
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

Cộng số bù 2 add rd, rs, rt

rd < rs+rt ;
Cộng không dấu addu rd, rs, rt

rd < rs+rt ;
Cộng với hằng số
16 bit bù 2
addi rd, rs, const

rd Å rs+const ;
Cộng với hằng số
16 bit không dấu
addiu rd, rs, const

rd Å rs+const ;
AND từng bit and rd, rs, rt

rd Å bitwise AND of rs with rt
AND từng bit với
hằng số 16 bit
andi rd, rs, const

rd Å bitwise AND of rs with
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

hay bằng 0
bgez rs, addr

nhảy đến addr nếu số bù 2 trong
rs >= 0. Cần thêm delay
Nhảy nếu nhỏ hơn
0
bltz rs, addr

nhảy đến addr nếu số bù 2 trong
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
Chia hai số bù 2 div rs, rt

lo Å rs div rt; hi Å rs mod rt
hai số rs và rt ở dạng bù 2
Chia hai số không
dấu
divu rs, rt

lo Å rs div rt; hi Å rs mod rt
hai số rs và rt ở dạng không dấu
Nhảy không điều
kiện
j target

delay sau một chu kỳ máy:

PC Å địa chỉ của target
Nhảy không điều
kiện và link
jal target

gọi hàm target, $ra = PC +4
PC Å địa chỉ của target, thêm
nop
Nhảy không điều
kiện theo giá trị
thanh ghi
jr rs

trở về hàm gọi, thường dùng
jr $ra , PC Å $ra, cần thêm lệnh
delay nop theo sau
Load byte và mở
rộng dấu
lb rd, offset(base)

rd Å byte đã được mở rộng dấu
từ ô nhớ có địa chỉ base +
offset, offset là số bù 2
Load byte và mở
rộng số 0 đầu
lbu rd, offset(base)

rd Å byte đã được mở rộng 0 từ
ô nhớ có địa chỉ base + offset,
offset là số bù 2

Load half-word
và mở rộng dấu
lh rd, offset(base)

rd Å 2 byte liên tiếp đã được
mở rộng dấu từ ô nhớ có địa chỉ
base + offset , offset là số bù 2
Load half-word
và mở rộng số 0
lhu rd, offset(base)

rd Å 2 byte liên tiếp đã được
mở rộng 0 từ ô nhớ có địa chỉ
base + offset, offset là số bù 2
Load hằng vào 16
bit cao
lui rd, const

2 byte cao của rd Å 16 bit const
2 byte thấp của rd Å 0x0000
Load word lw rd, offset(base)

rd Å word bộ nhớ có địa chỉ
base + offset, offset là số bù 2
Chuyển giá trị từ
hi vào thanh ghi
mfhi rd

rd Å hi
Chuyển giá trị từ

lo vào thanh ghi
mflo rd

rd Å lo
Nhân hai số bù 2 mult rs, rt

value(hi, lo) Å rs*rt; rs và rt là
hai số bù 2
Nhân hai số
không dấu
multu rs, rt

value(hi, lo) Å rs*rt; rs và rt là
hai số không dấu
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
OR từng bit or rd, rs, rt

rd Å OR từng bit của rs và rt
OR từng bit với
hằng 16 bit
or rd, rs, const

rd Å OR từng bit của rs và
hằng sau khi mở rộng 0
Lưu byte thấp của
thanh ghi vào bộ
nhớ

sb rs, offset(base)

byte ở offset +base Å byte thấp
của rs, offset dạng bù 2

Lưu hai byte thấp
vào bộ nhớ
sh rs, offset(base)


2 byte ở offset +base Å 2 byte
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
dấu
sll rd, rs, shft

rd Å rs sau khi dich trái shft
bit; 0 <= shft <32
So sánh hai thanh
ghi dạng bù 2
slt rd, rs, rt

if rs < rt
rd < 1
else
rd < 0
hai toán hạng dạng bù 2
So sánh thanh ghi
với hằng bù 2

slti rd, rs, const

if rs < const
rd < 1
else
rd < 0
hai toán hạng dạng bù 2
So sánh thanh ghi
với hằng không
dấu
sltiu rd, rs, const

if rs < const
rd < 1
else
rd < 0
hai toán hạng dạng không dấu
So sánh hai thanh
ghi không dấu
sltu rd, rs, rt

if rs < rt
rd < 1
else
rd < 0
hai toán hạng dạng không dấu
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
dạng bù 2
sub rd, rs, rt

rd Å rs – rt; các toán hạng dạng
bù 2
Trừ hai thanh ghi
dạng không dấu
subu rd, rs, rt

rd Å rs – rt; các toán hạng dạng
không dấu
Lưu thanh ghi vào
bộ nhớ
sw rs, offset(base)

word ở địa chỉ offset + base Å
$rs; offset dạng bù 2
XOR từng bit xor rd, rs, rt

rd Å XOR từng bit rs và rt
XOR từng bit với
hằng
xori rd, rs, rt


rd Å XOR từng bit rs và 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ụ:

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:

Chỉ thị (directive) được hỗ trợ gồm có:
Tên chỉ thị Ý nghĩa
.text <addr>
Tất cả các phần theo sau cho tới chỉ thị mới (.text, .ktext, .data, .kdata) được
đặt trong vùng nhớ chương trình (code segment). Tham số addr nếu có quy
định địa chỉ bắt đầu của vùng nhớ chương trình dùng để lưu các phần trong
phân đoạn mã chương trình này. Phần theo sau thường là các lệnh.
.ktext <addr>

Tất cả các phần theo sau cho tới chỉ thị mới (.text, .ktext, .data, .kdata) được
đặt vào vùng nhớ nhân (kernel) của hệ điều hành. Tham số addr nếu có quy
định địa chỉ bắt đầu của vùng nhớ dùng để lưu. Phần theo sau thường là các
lệnh.
.globl sym Khai báo nhãn sym là toàn cục và có thể được tham khảo từ file khác
.data <addr>
Tất cả các phần theo sau cho tới chỉ thị mới (.text, .ktext, .data, .kdata) được
đặt trong vùng nhớ dữ liệu nhân (kernel data segment). Tham số addr nếu
có quy định địa chỉ bắt đầu của vùng nhớ dùng để lưu.
newline \n
tab \t
quote \”
.data
item: .word 1
.text
.globl main # must be global
main:


.kdata <addr>
Tất cả các phần theo sau cho tới chỉ thị mới (.text, .ktext, .data, .kdata) được
đặt trong vùng nhớ dữ liệu (data segment). Tham số addr nếu có quy định
địa chỉ bắt đầu của vùng nhớ dùng để lưu.
.ascii str
Lưu chuỗi str vào bộ nhớ, không có ký tự kết thúc chuỗi (giá trị = 0) sau
cùng
.asciiz str
Lưu chuỗi str vào bộ nhớ, thêm ký tự kết thúc chuỗi (giá trị = 0) sau cùng
.byte b1, ,bn
Lưu n byte liên tiếp nhau b1, ,bn vào bộ nhớ

.half h1, ,hn
Lưu n phần tử 16-bit liên tiếp nhau h1, ,hn vào bộ nhớ
.word w1, ,wn
Lưu n phần tử 32-bit liên tiếp nhau w1, ,wn vào bộ nhớ
.float f1, ,fn
Lưu n số thực dấu chấm động độ chính xác đơn liên tiếp nhau f1, ,fn vào bộ
nhớ
.double d1, ,dn
Lưu n số thực dấu chấm động độ chính xác đơn liên tiếp nhau d1, ,dn vào
bộ nhớ
.space n
Cấp phát n byte liên tiếp nhau trong phân đoạn dữ liệu hiện tại. Phải đặt sau
chỉ thị .data
.extern sym n
Khai báo dữ liệu lưu ở sym có kích thước n byte và sym là nhãn toàn cục.
Dữ liệu vùng nhớ này được truy xuất thông qua thanh ghi $gp

5. Các chương trình mẫu
5.1 Các lệnh luận lý:

Thiết lập các tham số trong menu Simulator -> Settings:
Bare Machine ON, Allow Pseudo Instructions OFF, Load Trap File OFF, Delayed
Branches ON, Delayed Loads ON, Mapped IO OFF, Quiet OFF


5.2 Các lệnh số học:

Thiết lập các tham số trong menu Simulator -> Settings:
Bare Machine ON, Allow Pseudo Instructions OFF, Load Trap File OFF, Delayed
Branches ON, Delayed Loads ON, Mapped IO OFF, Quiet OFF

add addu div multu
addi sub divu mfhi
addiu subu mult mflo
and ori nor
andi xor sll
or xori srl
## Put the bit pattern 0x0000FACE into register $1.
.text
.globl main
main:
ori $1,$0,0xFACE # 0x0000FACE into $1

andi $2,$1,0x0F0F # 0x0A0E in $2

andi $3,$1,0x00F0 # 0x00C0 in $3
sll $3,$3,8 # 0xC000 in $3
or $2,$2,$3 # 0xCA0E in $2

andi $3,$1,0xF000 # 0xF000 in $3
srl $3,$3,8 # 0x00F0 in $3
or $2,$2,$3 # 0xCAFE in $3
# done
## End of file


5.3 Các lệnh thao tác bộ nhớ

Thiết lập các tham số trong menu Simulator -> Settings:
Bare Machine ON, Allow Pseudo Instructions OFF, Load Trap File OFF, Delayed
Branches ON, Delayed Loads ON, Mapped IO OFF, Quiet OFF

lb lui sb
lbu lw sh
lh lhu sw
## Program to calculate 3x**2 + 5x - 8
##
## Assumes that all results fit into 32 bits.
##
## Follows the hardware rule of keeping a new mult
## two instructions away from a previous mflo.
##
## Register Use:
## $8 x
## $9 result

.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



5.4 Các lệnh nhảy

Thiết lập các tham số trong menu Simulator -> Settings:
Bare Machine ON, Allow Pseudo Instructions OFF, Load Trap File OFF, Delayed
Branches ON, Delayed Loads ON, Mapped IO OFF, Quiet OFF

j bne slti
beq bltz sltu
bgez slt sltiu
## Find the Significant bits in a pattern
## The significant bits are the leftmost one bit and
## all its to its right
## Approach: Count the number of right shifts needed
## before the pattern becomes all zeros
##
## Register Use:
## $7 shift count
## $8 the bit pattern, 0x00298D7D for example.

.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
## copy $9 to memory in big-endian form
##
## Register Use:
## $8 first byte of the tape block
## $9 4-byte integer

.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

5.5 Các ví dụ xử lý chuỗi, xử lý mảng
Các lệnh đã biết cho đến giờ:


Thiết lập các tham số trong menu Simulator -> Settings:
Bare Machine ON, Allow Pseudo Instructions OFF, Load Trap File OFF, Delayed
Branches ON, Delayed Loads ON, Mapped IO OFF, Quiet OFF




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

exit: j exit # sponge for extra cycles
sll $0,$0,0

## End of file

add div mflo slt, slti
addi divu mult sltu, sltiu
addiu j multu sra
addu lb nor srl
and lbu or sub
andi lh ori subu
beq lhu sb sw
bgez lui sh xor

bltz lw sll xori
bne mfhi
## To Lower Case
##
##
## Write a program that converts the string to all lower case
## characters. Do this by adding 0x20 to each character in the string.

## Register Use:
##
## $8 current character
## $10 character pointer


5.6 Các lệnh mã giả mở rộng

Thiết lập các tham số trong menu Simulator -> Settings:
SPIM set Bare Machine OFF, Allow Pseudo Instructions ON, Load Trap File OFF,
Delayed Branches ON, Delayed Loads ON, Mapped IO OFF, Quiet OFF.
Ví dụ theo sau thực hiện việc tính hóa đơn bữa ăn:
tổng phí = tiền thức ăn + 8% tiền thuế +15% tiền phục vụ
.text
.globl main

main: lui $10,0x1000 # initialize base register of the string

lbu $8,($10) # get the first char of the string
sll $0,$0,0
loop:
beq $8,$0,halt # while ( char != '/0' )

sll $0,$0,0 #
addiu $8,$8,0x20 # uncapitalize char
sb $8,($10) # replace char in string
addiu $10,$10,1 # advance the char pointer
lbu $8,($10) # get the next char of the string
j loop # end while
sll $0,$0,0

halt: j halt # cycle sponge
sll $0,$0,0

.data
string: .asciiz "ABCDEFGHIJKLMNOP"

## End of file

move d,s # copy the contents of the source register s to the
# destination register d

li d,value # load register $d with the positive or negative
# integer "value". Value may be a 16 or a 32-bit integer.

lw d,exp # Load register $d with the value at address "exp".
# "exp" is often a symbolic address.

la d,exp # load register $d with the address described by the
# expression "exp". "exp" is often a symbolic address.

nop # no operation. do nothing for one machine cycle.


sw d,exp # Store register $d into the word at address exp.
# exp can be any of several expression types
# that evaluate to an address
mul d,s,t # multiply $s by $t. put the result in $d

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.



## rest.asm
##
## Total restaurant bill calculator
##
## Register Use:
##
## $s0 meal cost
## $s1 tip rate
## $s2 tax rate
## $s3 total rate
## $s4 tax+tip dollars
## $s5 total bill

.globl main

# Get meal cost

main: li $v0,4 # print prompt
la $a0,prompt
syscall
li $v0,5 # input meal cost
syscall
move $s0,$v0 # save it in $s0

# 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

move $a0,$s4 # get tax+tip
li $v0,1 # print integer
syscall #

li $v0,4 # print string
la $a0,head2 # "total cost"
syscall

move $a0,$s5 # get total
li $v0,1 # print integer

syscall #

li $v0,10 # exit
syscall

.data
tip: .word 15 # tip rate in percent
tax: .word 8 # tax rate in percent

prompt: .asciiz "Enter food cost: "
head1 : .asciiz " Tax plus tip: "
head2 : .asciiz "\n Total cost: "

5.7 Các chương trình xử lý xuất nhập
SPIM cung cấp các hàm hệ thống dùng để xử lý các thao tác xuất nhập sau:
Công việc Mã trong $v0 Tham số Giá trị trả về
print integer 1 $a0 == integer
print float 2 $f12 == float
print double 3 ($f12, $f13) == double
print string 4 $a0 == address of string
read integer 5 $v0 == integer
read float 6 $f0 == float
read double 7 ($f0, $f1) == double
read string 8 $a0 == buffer address
$a1 == buffer length

allocate memory 9 $a0 == number of bytes $v0 == address
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:


Thiết lập các tham số trong menu Simulator -> Settings:
Bare Machine OFF, Allow Pseudo Instructions ON, Load Trap File ON, Delayed
Branches ON, Delayed Loads ON, Mapped IO OFF, Quiet OFF
Ví dụ sau in ra mẫu thư nhắc nhở trả sách một cách tự động sau khi người dùng nhập tên
người mượn sách trả trễ:
# 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.

li $v0,10 # code 10 == exit
syscall # Halt the program.

.data
string: .asciiz "Hello SPIM!\n"
# end of file

5.8 Dùng stack tính toán biểu thức
Thanh ghi stack pointer ($sp) dùng để quản lý stack. Thanh ghi này chỉ đến phần tử trên
đỉnh ở vùng nhớ stack. Khi chương trình bắt đầu chạy, $sp có giá trị khởi tạo
0x7FFFFFFC. Vùng nhớ stack mở rộng xuống dưới đồng nghĩa giá trị thanh ghi $sp
giảm đi.
Tác vụ PUSH phần tử vào stack tiến hành hai việc:
• Thứ nhất, thay đổi giá trị thanh ghi $sp để trỏ đến phần tử đỉnh mới
• Thứ hai, lưu giá trị vào vị trí đỉnh mới

Tương tự, tác vụ POP phần tử ra stack tiến hành hai việc:
• Thứ nhất, lưu giá trị phần tử đỉnh stack vào biến
• Thứ hai, thay đổi giá trị thanh ghi $sp trỏ đến phần tử đỉnh mới

# overdue.asm

.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.

# print the letter
li $v0,4 # print greeting
la $a0,letter #
syscall
li $v0,4 # print body
la $a0,body #
syscall

li $v0,10 # exit
syscall


.data
prompt: .asciiz "enter name, followed by comma-enter: "
letter: .ascii "\n\nDear "
name: .space 24

body: .ascii "\nYour library books are way\n"
.ascii "overdue. Please return them\n"
.ascii "before we give your name\n"
.ascii "to the enforcement squad.\n"

# end of file



Thiết lập các tham số trong menu Simulator -> Settings:
Bare Machine OFF, Allow Pseudo Instructions ON, Load Trap File ON, Delayed
Branches ON, Delayed Loads ON, Mapped IO ON, Quiet OFF


# POP the item into $t0:
lw $t0,($sp) # Copy top the item to $t0.
addiu $sp,$sp,4 # Point to the item beneath the old top.

# PUSH the item in $t0:
addiu $sp,$sp,-4 # point to the place for the new item,
sw $t0,($sp) # store the contents of $t0 as the new top.

# Evaluate the expression ab - 12a + 18b - 7
#
# Settings: Load delays OFF; Branch delays OFF,

# Trap file ON; Pseudoinstructions ON

.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)

5.9 Cách gọi hàm đơn giản
Lệnh jal dùng để gọi hàm, lệnh jr dùng để trở về từ hàm được gọi. Lệnh nop cần được
thêm vào sau các lệnh jal và jr

Các chú ý về cách gọi hàm đơn giản:
o Thủ tục con được gọi bởi lệnh
jal.
o Thủ tục con không gọi thủ tục con khác.

o Thủ tục con trở về chương trình gọi bằng lệnh
jr $ra.
o Các thanh ghi được sử dụng:
 $t0 - $t9 — Thủ tục con tự do sử dụng.

$s0 - $s7 — Thủ tục con không được thay đổi giá trị sau khi trở về.
jal sub # $ra <― PC+4 (the address 8 bytes away from the jal)
# PC <― sub load the PC with the subroutine entry point
# a branch delay slot follows this instruction
jr $ra # PC <― $ra
# A branch delay slot follows this instruction.



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 -12a
addu $sp,$sp,4
addu $t1,$t1,$t0 # -12a + 18b -7

lw $t0,($sp) # pop ab
addu $sp,$sp,4
addu $t1,$t1,$t0 # ab - 12a + 18b -7

done: li $v0,1 # print sum
move $a0,$t1
syscall

li $v0,10 # exit
syscall

.data
a: .word 0
bb: .word 10

 $a0 - $a3 — Chứa các tham số cho thủ tục con. Thủ tục con có thể thay đổi
các thanh ghi này.
 $v0 - $v1 — Chứa các giá trị trả về từ thủ tục con.
o Thủ tục main trả điều khiển bằng cách sử dụng hàm exit của hệ thống.
Thiết lập các tham số trong menu Simulator -> Settings:
Bare Machine OFF, Allow Pseudo Instructions ON, Load Trap File ON, Delayed
Branches ON, Delayed Loads ON, Mapped IO ON, Quiet OFF
Sau đây là chương trình đọc 3 số nguyên và in ra tổng của chúng:


# read in three integers and print their sum
#

.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

addu $s0,$s0,$s1 # compute the sum
addu $a0,$s0,$s2

li $v0,1 # print the sum
syscall

li $v0,10 # exit
syscall

# pread prompt for and read an integer
# on entry:
# $ra return address
# on exit:
# $v0 the integer
.text
.globl pread
pread:
la $a0,prompt # print string
li $v0,4 # service 4
syscall

li $v0,5 # read int
syscall # service 5

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:

Thiết lập các tham số trong menu Simulator -> Settings:
Bare Machine OFF, Allow Pseudo Instructions ON, Load Trap File ON, Delayed
Branches ON, Delayed Loads ON, Mapped IO ON, Quiet OFF.
Sau đây là ví dụ chương trình yêu cầu người dùng nhập vào 2 số X, Y. Chương trình thực
hiện việc tính toán các biểu thức X*X, X*Y, 5*Y và in ra kết quả lớn nhất từ giá trị của 3
biểu thức trên. Chương trình main gọi thủ tục con maxExp tính và tìm ra giá trị lớn nhất
từ ba biểu thức X*X, X*Y và 5*Y sau khi đã có giá trị X, Y. Thủ tục con maxExp gọi
thủ tục con maxInt hai lần để so sánh X*X với X*Y, kết quả sẽ so sánh với 5*Y để cho ra
kết quả yêu cầu.
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.
Phần đầu thủ tục con (Trong thủ tục con):
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.
Thân thủ tục con:
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):
8. Đưa các giá trị cần tr

ả về vào $v0-$v1
9. Pop ra khỏi stack (theo thứ tự ngược) các thanh ghi $s0-$s7 đã lưu ở bước 5.
10. Pop ra khỏi stack địa chỉ trở về $ra nếu thực hiện bước 4
11. Trở về chương trình gọi sử dụng jr $ra.
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.

## Driver main program for the application

.text
.globl main

main:
sub $sp,$sp,4 # push the return address
sw $ra,($sp)
sub $sp,$sp,4 # push $s0
sw $s0,($sp)

la $a0,xprompt # prompt the user
li $v0,4 # service 4
syscall
li $v0,5 # service 5 read int
syscall # $v0 = integer
move $s0,$v0 # save x

la $a0,yprompt # prompt the user
li $v0,4 # service 4
syscall

li $v0,5 # service 5 read int
syscall # $v0 = integer

# prepare arguments
move $a0,$s0 # x
move $a1,$v0 # y
jal maxExp # maximum expression
nop # returned in $v0
move $s0,$v0 # keep it safe

la $a0,rprompt # output title
li $v0,4 # service 4
syscall

move $a0,$s0 # get maximum
li $v0,1 # print it out
syscall

lw $ra,($sp) # pop $s0
add $s0,$sp,4
lw $ra,($sp) # pop return address
add $sp,$sp,4

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

move $a0,$s0 # compute max of x*x
move $a1,$s1 # and x*y
jal maxInt # current max in $v0
nop

move $a0,$v0 # compute max of

move $a1,$s2 # current max, and 5*y
jal maxInt # total max will be in $v0
nop

5.11 Quản lý biến cục bộ và gọi hàm sử dụng stack pointer và frame pointer
Thủ tục con có thể sử dụng stack để tính toán các biểu thức (5.8), thanh ghi sp có thể thay
đổi nên khó sử dụng thanh ghi này để quản lý biến cục bộ cố định. Để quản lý biến cục
bộ một cách dễ dàng, MIPS sử dụng thanh ghi fp làm địa chỉ nền cố định để quản lý biến
cục bộ trong quá trình thực thi.
Hình bên minh họa việc dùng thanh ghi fp để quản lý biến cục bộ với các giả sử sau:
• Thủ tục cha gọi thủ tục con;
• Thủ tục cha sử dụng các thanh ghi tạm $t0,
$t3 trước khi gọi thủ tục con;
• Thủ tục con sử dụng các thanh ghi $s0, $s3,
$s5;
• Thủ tục con gồm 4 biến cục bộ a, b, i, j



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.
Phần đầu thủ tục con (Trong thủ tục con):
4. Push $ra vào stack.
5. Push thanh ghi $fp của thủ tục gọi
6. Push vào stack các thanh ghi $s0-$s7 nếu thủ tục con này có thể thay
đổi chúng.

7. Khởi tạo $fp = $sp – không gian cần cho biến cục bộ (4*số biến cục bộ).
8. Khởi tạo $sp = $fp
Thân thủ tục con:
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):
13. Đưa các giá trị cần trả về vào $v0-$v1
14. $sp = $fp + không gian cần cho biến cục bộ
15. Pop ra khỏi stack (theo thứ tự ngược) các thanh ghi $s0-$s7 đã lưu ở bước 5.
16. Pop ra khỏi stack địa chỉ trở về $ra.
17. Pop ra khỏi stack
địa chỉ trở về $ra
18. Trở về chương trình gọi sử dụng jr $ra.
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.

Thiết lập các tham số trong menu Simulator -> Settings:
Bare Machine OFF, Allow Pseudo Instructions ON, Load Trap File ON, Delayed
Branches ON, Delayed Loads ON, Mapped IO ON, Quiet OFF.



# 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,4 # write("enter an int:")
la $a0,prompt1
syscall

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 )

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×