Lập Trình Hợp Ngữ ARM - Bài 6

6. Cấu trúc dữ liệu
Chúng ta đã gặp một số các cách thức mà dữ liệu được truyền giữa các phần của một chương trình: lập luận và dẫn kỹ thuật qua các chương trước.
Trong chương này, chúng tôi tập trung hơn vào những cách thức mà các cấu trúc dữ liệu toàn cầu được lưu trữ, và cung cấp cho thói quen ví dụ cho thấy các kỹ thuật thao tác dữ liệu điển hình.
Dữ liệu có thể được phân loại như là nội bộ hay bên ngoài. Đối với mục đích của chúng tôi, chúng tôi sẽ coi dữ liệu nội bộ như các giá trị được lưu trữ trong sổ đăng ký hoặc 'trong' chương trình riêng của mình. Dữ liệu bên ngoài được lưu trữ trong bộ nhớ được phân bổ một cách rõ ràng bằng một cuộc gọi đến một thói quen quản lý bộ nhớ hệ điều hành, hoặc trên stack.
6.1 Viết cho ROM
Một chương trình sử dụng các dữ liệu bộ nhớ trong có thể phải được hạn chế để các giá trị đọc. Nếu bạn đang viết một chương trình mà một ngày có thể được lưu trữ trong bộ nhớ ROM, chứ không phải được nạp vào bộ nhớ RAM, bạn phải nhớ rằng thực hiện một lệnh chẳng hạn như:
STR R0, nhãn
sẽ không có hiệu quả mong muốn nếu chương trình được thực hiện trong ROM. Vì vậy, bạn cần phải hạn chế tài liệu tham khảo nội bộ để nhìn lên bảng, vv nếu bạn muốn mã của bạn để được ROMmable. Ví dụ, các thông dịch viên BBC BASIC chỉ truy cập các địa điểm nội bộ cho chương trình khi thực hiện các tác vụ như đọc các bảng từ khóa hoặc giúp thông tin.
Một hạn chế liên quan trên mã ROM là nó không chứa bất kỳ hướng dẫn tự sửa đổi. đang tự sửa đổi đôi khi được sử dụng để thay đổi một hướng dẫn trước khi nó được thực thi, ví dụ để thực hiện một số hoạt động chi nhánh phức tạp. kỹ thuật như vậy được coi là xấu thực tế, và một cái gì đó để tránh được, thậm chí trong các chương trình RAM. Rõ ràng là nếu bạn đang bị cám dỗ để viết mã tự thay đổi, bạn sẽ phải đối phó với một số lỗi khá tối nghĩa nếu chương trình là bao giờ ROMmed.
Cuối cùng, sự cần thiết cho vị trí độc lập là một cân nhắc quan trọng khi bạn viết mã cho ROM. Một con chip ROM có thể được trang bị tại bất kỳ địa chỉ trong không gian địa chỉ ROM của máy, và vẫn cần được dự kiến ​​làm việc.
Thời gian chỉ có nó là an toàn để viết thư cho các khu vực chương trình là trong các chương trình mà sẽ luôn luôn, luôn luôn, có bộ nhớ RAM trên, ví dụ như tiện ích nhỏ để được nạp từ đĩa. Trong thực tế, các chương trình, ngay cả bộ nhớ RAM trên không hoàn toàn miễn dịch từ vấn đề này. Các MEMC điều khiển chip nhớ được sử dụng trong nhiều hệ thống ARM có khả năng làm cho một khu vực của bộ nhớ 'chỉ đọc'. Điều này là để bảo vệ các chương trình từ hơn-viết chính nó, hoặc các chương trình khác trong một hệ thống đa tác vụ. Cố gắng viết thư cho một khu vực như vậy sẽ dẫn đến hủy bỏ , như mô tả trong Chương Bảy.
Đó là một ý tưởng tốt, sau đó, để chỉ sử dụng bộ nhớ RAM mà đã được phân bổ một cách rõ ràng là không gian làm việc của hệ điều hành, và đối xử với khu vực chương trình như "chỉ đọc".
6.2 Các loại dữ liệu
Việc giải thích của một chuỗi các bit trong bộ nhớ là hoàn toàn để các lập trình viên. Giả định chỉ có bộ xử lý làm là khi nó tải một lời từ bộ nhớ giải quyết bằng chương trình truy cập, từ đó là một hướng dẫn ARM hợp lệ.
Trong phần này, chúng tôi thảo luận về các loại phổ biến của dữ liệu được sử dụng trong các chương trình, và làm thế nào họ có thể được lưu trữ.
6.3 Nhân vật
Đây có lẽ là kiểu dữ liệu phổ biến nhất, như thông tin liên lạc giữa các chương trình và người dân thường là định hướng nhân vật. Một nhân vật là một số nguyên nhỏ mà giá trị được sử dụng để đại diện cho một biểu tượng đặc biệt. Một số ký tự được sử dụng để đại diện cho thông tin điều khiển thay vì biểu tượng, và được gọi là mã số kiểm soát.
Bởi đến nay các đại diện nhân vật phổ biến nhất là ASCII - American Mã tiêu chuẩn cho trao đổi thông tin. Chúng tôi sẽ chỉ liên quan với ASCII trong cuốn sách này.
mã ASCII chuẩn bảy bit - đại diện cho 128 giá trị khác nhau. Những người trong phạm vi đứng 32..126 cho các ký hiệu in: các chữ cái, chữ số, ký hiệu dấu chấm câu, vv Một ví dụ là 65 (& 41), viết tắt cho chữ chữ hoa A. Phần còn lại 0..31 và 127 là kiểm soát mã số. Các mã này không đại diện cho nhân vật chất, nhưng được sử dụng để điều khiển các thiết bị đầu ra. Ví dụ, đoạn code 13 (& 0D) được gọi là vận chuyển trở lại và gây ra một thiết bị đầu ra để di chuyển đến đầu dòng hiện tại.
Bây giờ, chiều rộng tiêu chuẩn cho một byte là tám bit, vì vậy khi một byte được sử dụng để lưu trữ một ký tự ASCII, có một chút rảnh rỗi. Trước đó (tức là trong những ngày băng đục lỗ) này đã được sử dụng để lưu trữ một bit chẵn lẻ của các nhân vật. Điều này được sử dụng để làm cho số lượng 1 bit trong mã số chẵn (hoặc lẻ). Điều này được gọi là chẵn (hoặc lẻ) chẵn lẻ. Ví dụ, nhị phân của mã cho chữ A là 1000001. Điều này có một số thậm chí của một bit, do đó các bit chẵn lẻ sẽ là 0. Như vậy mã bao gồm chẵn lẻ cho A là 01000001. Mặt khác, các mã cho C là 1000011, trong đó có một số lẻ của 1s. Để làm điều này thậm chí, chúng tôi sẽ lưu trữ C với tính chẵn lẻ như 11000011. chẵn lẻ cho một hình thức đơn giản của việc kiểm tra là các nhân vật đã được gửi đi mà không có lỗi trên đường truyền.
Là thiết bị đầu ra đã trở nên phức tạp hơn và có thể hiển thị nhiều hơn 95 ký tự hạn chế của ASCII thuần khiết, bit thứ tám của mã số nhân vật đã thay đổi trong sử dụng. Thay vì chút lưu trữ chẵn lẻ này, nó thường biểu thị khác 128 ký tự, các mã mà nằm trong phạm vi 128..255. mã như vậy thường được gọi là ký tự 'top-bit-set', và đại diện cho các biểu tượng như chữ nước ngoài, bảng chữ cái Hy Lạp, biểu tượng 'hộp vẽ nhân vật và các ký hiệu toán học.
Có một tiêu chuẩn (đặt ra bởi ISO, Tổ chức Tiêu chuẩn Quốc tế) cho mã hàng đầu-bit-set trong khoảng 160..255. Trong thực tế có rất nhiều bộ ký tự, được thiết kế cho mục đích sử dụng khác nhau. Dự kiến, nhiều máy mới, bao gồm cả những người dựa trên ARM sẽ áp dụng tiêu chuẩn này.
Việc sử dụng các bit đầu của một byte để biểu thị một bộ thứ hai của mã ký tự không loại trừ việc sử dụng các tính chẵn lẻ. Nhân vật chỉ đơn giản là gửi qua đường truyền như là tám bit cộng với tính chẵn lẻ, nhưng chỉ được lưu trữ trong bộ nhớ như tám bit.
Khi được lưu trữ trong bộ nhớ, các nhân vật thường được đóng gói từ bốn đến mỗi từ ARM. Nhân vật đầu tiên được tổ chức tại byte thấp của từ, lần thứ hai trong các byte tiếp theo, và như vậy. Chương trình này giúp việc xử lý hiệu quả các đặc điểm cá nhân bằng cách sử dụng LDRB và STRB hướng dẫn.
Trong sổ đăng ký, ký tự thường được lưu trữ trong byte thấp, ba byte khác được thiết lập để không. Điều này rõ ràng là không khôn ngoan như LDRB zero bit 8..31 của thanh ghi đích của nó, và STRB sử dụng bit 0..7 của thanh ghi nguồn là dữ liệu của nó.
hoạt động chung về đăng ký được dịch và loại thử nghiệm. Chúng tôi bao gồm dịch dưới đây sử dụng chuỗi ký tự. Loại thử nghiệm liên quan đến việc khám phá ra nếu một nhân vật là thành viên của một tập hợp. Ví dụ, bạn có thể muốn xác định nếu một nhân vật là một bức thư. Trong các chương trình mà thực hiện nhiều thao tác nhân vật, người ta thường tìm thấy một tập hợp các chức năng mà trả về kiểu của các nhân vật trong một đăng ký tiêu chuẩn, ví dụ như R0.
Những chức năng loại kiểm nghiệm, hoặc vị ngữ, thường được đặt tên như isLower (trường hợp) hoặc isDigit , và trả về một lá cờ chỉ có các nhân vật là một thành viên của loại đó. Chúng tôi sẽ áp dụng các quy ước rằng nhân vật trong R0 về nhập cảnh, xuất cảnh và trên tất cả các thanh ghi này được bảo quản, và cờ carry được xóa nếu các nhân vật trong các bộ đặt tên, hoặc thiết lập nếu nó không phải là. Dưới đây là một vài ví dụ: isLower và isDigit :
Org DIM 100
sp = 1
link = 14
WriteI = & 100
Newline = 3
Cflag = & 20000000: Mask REM cho carry cờ
CHO qua = 0 TO 2 Bước 2
P% = org
[opt qua
;
.; Character loại kiểm nghiệm vị
; Lượt vào , R0 chứa các ký tự để được kiểm tra
; Mở lối C = 0 nếu nhân vật trong bộ này, C = 1 nếu không
, tất cả các đăng ký bảo quản
;
.isLower
CMP R0, #ASC "a"; Kiểm tra giới hạn dưới
BLT predFail; Ít hơn để trở lại thất bại
CMP R0, #ASC "z" 1; Kiểm tra giới hạn trên
MOV pc, liên kết; Return với carry thích hợp
.predFail
TEQP pc, #Cflag; Thiết carry cờ
MOV pc, liên kết và nộp lại
;
.isDigit
CMP R0, # ASC "0"; Kiểm tra giới hạn dưới
BLT predFail; hạ nên không
CMP R0, #ASC "9" 1; Kiểm tra giới hạn trên
MOV pc, liên kết; Return với Carry thích hợp
;
; Test cho isLower và isDigit
; Nếu R0 là chữ số, 0 in; nếu trường hợp thấp hơn, một in
;
.testPred
STMFD (sp), {link}!
BL isDigit
SWICC WriteI + ASC "0"
BL isLower
SWICC WriteI + ASC "một"
SWI Newline
LDMFD (sp), {} pc!
;
]
TIẾP vượt qua
REPEAT
INPUT "nhân vật", c $
A% = ASCC $
CALL testPred
ĐẾN FALSE
Chương trình sử dụng hai phương pháp khác nhau để thiết lập các cờ carry cho nhà nước yêu cầu. Việc đầu tiên là sử dụng TEQP . Nhớ lại từ Chương Ba rằng điều này có thể được sử dụng để trực tiếp đặt các bit của thanh ghi trạng thái từ các toán hạng bên phải. Biến Cflag được thiết lập để & 20000000, đó là mặt nạ bit cho lá cờ carry trong đăng ký hộ. Do đó, hướng dẫn
TEQP pc, #Cflag
sẽ thiết lập các cờ carry và đặt lại các phần còn lại của những lá cờ kết quả. Phương pháp thứ hai sử dụng thực tế là CMP hướng dẫn đặt cờ carry khi <LHS> là lớn hơn hoặc bằng của nó <RHS> . Vì vậy, khi kiểm tra cho chữ thường, sự so sánh
CMP R0, # ASC "z" 1
sẽ thiết lập các cờ carry nếu R0 là lớn hơn hoặc bằng mã ASCII của z cộng 1. Đó là, nếu R0 là lớn hơn so với mã cho z, carry sẽ được thiết lập, và nếu nó là nhỏ hơn hoặc bằng với nó (và do đó là một bức thư viết thường), carry sẽ được rõ ràng. Đây chính là cách mà chúng tôi muốn nó được thiết lập để cho biết R0 chứa một chữ thường hay không.
Xâu ký tự
Khi một tập hợp các ký tự được lưu trữ liên tục kế nhau trong bộ nhớ, trình tự thường được gọi là một chuỗi. Có đại diện khác nhau cho các chuỗi, phân biệt bằng cách họ chỉ ra số ký tự được sử dụng. Một kỹ thuật phổ biến là để chấm dứt chuỗi bằng một nhân vật được xác định trước. BBC BASIC sử dụng các nhân vật trở về vận chuyển & 0D để đánh dấu sự kết thúc của nó $ điều hành gián tiếp chuỗi. Ví dụ, chuỗi "ARMENIA" sẽ được lưu trữ như các byte
A & 41
R & 52
M & 4D
E & 45
N & 4E
I & 49
A & 41
cr & 0D
Một hạn chế rõ ràng của kiểu chuỗi là nó không thể chứa ký tự dấu phân cách.
Kỹ thuật phổ biến khác là để lưu trữ độ dài của chuỗi ngay lập tức trước khi những nhân vật - BCPL ngôn ngữ thông qua kỹ thuật này. Chiều dài có thể chiếm một hoặc nhiều byte, tùy thuộc vào bao lâu một chuỗi có được đại diện. Bằng cách hạn chế nó đến một byte duy nhất (độ dài giữa 0 và 255 ký tự), bạn có thể giữ lại các tài sản của các ký tự byte liên kết. Nếu, nói, toàn bộ một từ được sử dụng để lưu trữ độ dài, sau đó toàn bộ chuỗi phải được từ kết nếu chiều dài là để được truy cập thuận tiện. Dưới đây là một ví dụ về cách chuỗi "Vũ khí" sẽ được lưu trữ bằng cách sử dụng thời gian một byte:
len & 08
A & 41
R & 52
M & 4D
A & 41
M & 4D
E & 45
N & 4E
T & 54
Rõ ràng chuỗi được lưu trữ với độ dài của chúng có thể chứa bất kỳ ký tự.
hoạt động phổ biến trên dây là: sao chép một chuỗi từ nơi này đến nơi khác, kể chiều dài của một chuỗi, biểu diễn một bản dịch vào các ký tự của một chuỗi, việc tìm kiếm một phụ chuỗi của một chuỗi, so sánh hai chuỗi, cách ghép hai chuỗi. Chúng tôi sẽ đề cập đến một số trong số này trong phần này. Hai hoạt động phổ biến khác được chuyển đổi từ nhị phân để ASCII đại diện của một số, và ngược lại. Chúng được mô tả trong các phần tiếp theo.
dịch nhân vật
Dịch liên quan đến việc thay đổi một số hoặc tất cả các ký tự trong một chuỗi. Một bản dịch phổ biến là việc chuyển đổi chữ thường thành chữ hoa, hoặc ngược lại. Này được sử dụng, ví dụ, để lực lượng tên tập tin vào trường hợp trên. Một hình thức khác của bản dịch được chuyển đổi giữa mã số nhân vật khác nhau, ví dụ như ASCII và EBCDIC ít phổ biến.
Ở trang sau là một thói quen mà chuyển đổi các chuỗi ở strPtr vào trường hợp trên. Các chuỗi được giả định là chấm dứt bởi CR.
DIM org 100, buff 100
cr = & 0D
strPtr = 0
sp = 13
link = 14
carryBit = & 20000000
CHO qua = 0 TO 2 Bước 2
P% = org
[opt qua
; toUpper. Chuyển đổi các ký tự trong chuỗi ở strPtr
; đến trường hợp trên. Tất cả các nhân vật khác không thay đổi.
; Tất cả đăng ký bảo quản
; R1 sử dụng tạm thời cho nhân vật
;
toUpper
STMFD (sp), {R1, strPtr, link};! Preserve đăng ký
.toUpLp
LDRB R1, [strPtr], # 1; Nhận byte và inc ptr
CMP R1, #cr;? Kết thúc chuỗi
LDMEQFD (sp), {R1, strPtr, pc};! Vâng, vì vậy trở
BL isLower; Kiểm tra trường hợp thấp hơn
BCS toUpLp; không phải là, do đó vòng lặp
SUB R1, R1, #ASC "một" -ASC "A"; Chuyển đổi các trường hợp
STRB R1, [strPtr, # - 1]; Lưu char lại
B toUpLp; Tiếp theo char
;
.isLower
CMP R1, #ASC "một"
BLT notLower
CMP R1, # ASC "z" +1
MOV pc, liên kết
.notLower
TEQP pc, # carryBit
MOV pc, link
]
TIẾP
REPEAT
INPUT "string", $ da bò
A% = da bò
CALL toUpper
IN "trở thành" $ da bò
ĐẾN FALSE
Chương trình sử dụng thực tế là trên và dưới trường hợp chữ có một sự khác biệt liên tục trong mã của mình theo bộ ký tự ASCII. Đặc biệt, mỗi chữ thường có một mã là cao hơn so với trường hợp trên của nó tương đương 32. Điều này có nghĩa rằng một khi nó đã được xác định rằng một nhân vật thực sự là một lá thư, nó có thể được thay đổi để các trường hợp khác bằng cách thêm hoặc trừ đi 32. Bạn cũng có thể trao đổi các trường hợp bằng cách sử dụng hoạt động này:
EOR R0, R0, #ASC "một" -ASC "A"; trường hợp hoán đổi
Các EOR hướng dẫn sẽ đảo ngược các bit trong mã ASCII mà quyết định trường hợp của bức thư.
So sánh chuỗi
Những thói quen ví dụ trong phần này so sánh hai chuỗi. So sánh chuỗi hoạt động như sau. Nếu dây là như nhau trong chiều dài và trong mỗi nhân vật, họ đều bình đẳng. Nếu họ là như nhau cho đến cuối của chuỗi ngắn hơn, thì đó là chuỗi ít. Nếu họ là như nhau cho đến khi một nhân vật nào đó, mối quan hệ giữa các chuỗi là giống như giữa các ký tự tương ứng ở vị trí đó.
strcmp dưới đây so sánh hai chuỗi byte-count tại str1 và str2 , và trả về với những lá cờ đặt theo các mối quan hệ giữa chúng. Đó là, lá cờ không được thiết lập nếu chúng bằng nhau, và cờ carry được thiết lập nếu str1 là lớn hơn hoặc bằng str2 .
DIM org 200, buff1 100, buff2 100
REM str1 để KÝ_TỰ2 nên đăng ký tiếp giáp
str1 = 0
str2 = 1
len1 = 2
len2 = 3
index = 4
cờ = len2
KÝ_TỰ1 = 5
KÝ_TỰ2 = 6
sp = 13
link = 14
CHO qua = 0 TO 2 Bước 2
P% = org
[opt qua
; strcmp. So sánh các dây tại str1 và str2. Xuất cảnh,
; tất cả các đăng ký bảo quản, cờ thiết lập để phản ánh
; mối quan hệ giữa các dây.
; Đăng ký sử dụng:
; len1, len2 - chiều dài chuỗi. len1 là ngắn hơn
; cờ - một bản sao của lá cờ từ so sánh chiều dài
, chỉ số - những nhân vật hiện tại trong chuỗi
; KÝ_TỰ1, KÝ_TỰ2 - ký tự từ một chuỗi
; NB len2 và cờ có thể được đăng ký cùng
;
.strCmp
; Lưu tất cả các đăng ký
STMFD (sp) !, {str1-KÝ_TỰ2, link}
LDRB len1, [str1], # 1; Lấy hai chiều dài
LDRB len2, [str2], # 1, và di chuyển con trỏ trên
CMP len1, len2; Tìm ngắn hơn
MOVGT len1, len2; Nhận ngắn trong len1
cờ MOV, pc; Lưu kết quả
MOV index, # 0; chỉ số Init
.strCmpLp
CMP chỉ số, len1; End của chuỗi ngắn hơn?
BEQ strCmpEnd; Có như vậy kết quả trên độ dài
KÝ_TỰ1 LDRB, [str1 , index]; Nhận một nhân vật từ mỗi
KÝ_TỰ2 LDRB, [str2, index]
ADD chỉ số, chỉ số, # 1, chỉ số tiếp
CMP KÝ_TỰ1, KÝ_TỰ2; So sánh các ký tự
BEQ strCmpLp; Nếu bằng nhau, char tiếp theo
;
; Return với kết quả của cuối cùng nhân vật so sánh
; cờ cửa hàng để BASIC có thể đọc chúng
;
STR pc, theFlags
LDMFD (sp), {str1-KÝ_TỰ2, pc}!
;
; chuỗi ngắn kiệt sức để trở lại với kết quả của
, phép so sánh giữa độ dài
;
.strCmpEnd
cờ TEQP, # 0; Nhận cờ từ đăng ký
;
; cờ cửa hàng để BASIC có thể đọc chúng
;
STR pc, theFlags
LDMFD (sp) !, {str1-KÝ_TỰ2, pc}
;
.theFlags
EQUD 0
]
TIẾP vượt qua
carryBit = & 20000000
zeroBit = & 40000000
REPEAT
Input ' "string 1", s1 $, "string 2", s2 $
buff1 = LENs1 $:? buff2 = LENs2 $
$ (buff1 + 1) = s1 $
$ (buff2 + 1) = s2 $
A% = buff1
B % = buff2
CALL strcmp
res theFlags =!
IN "string 1"
NẾU res VÀ carryBit THEN PRINT "> ="; IN ELSE "<";
IN "String 2"
IN "String 1";
IF res VÀ zeroBit THEN PRINT "="; IN ELSE "<>";
IN "String 2"
ĐẾN FALSE
Tìm một tiểu chuỗi
Trong các ứng dụng văn bản xử lý, đôi khi chúng ta cần phải tìm sự xuất hiện của một chuỗi trong một. Các chức năng BASIC INSTR gói gọn ý tưởng này.
Cuộc gọi
INSTR ( "STRING VỚI MẪU", "MẪU")
sẽ trả lại số nguyên 13, như các tiểu chuỗi " MẪU " xảy ra ở nhân vật 13 số đầu tiên.
Những thói quen được liệt kê dưới đây thực hiện một chức năng tương tự như INSTR . Nó có hai đối số - byte-count chuỗi con trỏ - và trả về vị trí mà tại đó các chuỗi thứ hai xảy ra trong lần đầu tiên. Ký tự đầu tiên của chuỗi là nhân vật 1 (như trong BASIC). Nếu phụ chuỗi không xuất hiện trong chuỗi chính, 0 được trả về.
Đối với một sự thay đổi, chúng tôi sử dụng ngăn xếp để vượt qua các tham số và trả về kết quả. Đó là vào người gọi để đặt chỗ cho kết quả dưới các đối số, và để 'dọn' chồng trở về.
DIM org 400, mainString 40, chuỗi 40
str1 = 0
str2 = 1
kết quả = 2
len1 = 3
len2 = 4
KÝ_TỰ1 = 5
KÝ_TỰ2 = 6
index = 7
làm việc = 8
sp = 13
link = 14
CHO qua = 0 TO 2 BƯỚC 2
P% = org
[opt qua
;
; InStr. Tìm thấy sự xuất hiện của str2 trong str1. Lập luận trên
; stack. Nhập cảnh, xuất cảnh, stack chứa:
;
; Kết quả từ 2
; str1 từ 1
; str2 <- sp từ 0 cộng với 10 bị đẩy từ
;
; str1 là chuỗi chính, str2 xâu
; Tất cả các thanh ghi này được bảo tồn. Kết quả là 0 cho không phù hợp
;
.instr
; Lưu việc đăng ký
! STMFD (sp), {str1 làm việc, liên kết}
LDR str1, [sp, # (công việc str1 + 2 + 0) * 4]; Nhận con trỏ str1
LDR str2, [sp, # (công việc str1 + 2 + 1) * 4]; và con trỏ str2
MOV việc, str1; Lưu để bù đắp tính
LDRB len1, [str1], # 1; Nhận độ dài và inc con trỏ
LDRB len2, [ str2], # 1
.inLp1
CMP len1, len2; kiểm tra nhanh cho thất bại
BLT inFail; substr dài hơn chính chuỗi
chỉ số MOV, # 0; Index thành chuỗi
.inLp2
CMP chỉ số, len2; Kết thúc chuỗi con?
BEQ inSucc; Vâng, vì vậy trở lại với str2
chỉ số CMP, len1
BEQ inNext; Kết thúc chuỗi chính thử để tiếp
LDRB KÝ_TỰ1, [str1, index]; So sánh nhân vật
LDRB KÝ_TỰ2, [str2, index]
ADD chỉ số, chỉ số, # 1; Inc chỉ số
CMP KÝ_TỰ1, KÝ_TỰ2 ; Có phải họ bằng?
BEQ inLp2; Vâng, vậy char tiếp theo
.inNext
ADD str1, str1, # 1; Di chuyển lên bắt đầu tiếp theo trong str2
len1 SUB, len1, # 1; Đó là một trong ngắn hơn bây giờ
B inLp1
.inFail
MOV việc, str1; Hãy SUB dưới đây cung cấp cho 0
.inSucc
SUB str1, str1, làm việc; Calc. pos. phụ chuỗi
str1 STR, [sp, # (công việc str1 + 2 + 2) * 4]; Lưu nó vào kết quả
; Khôi phục tất cả mọi thứ và trở lại
! LDMFD (sp), {str1 làm việc, máy tính}
;
; Ví dụ gọi InStr.
; Lưu ý rằng để mà STM đẩy
; đăng ký theo thứ tự dự kiến bởi InStr, sau đây
; mối quan hệ phải tồn tại. str2 <str1 <kết quả
;
.testInstr
ADR str1, mainString; Địa chỉ của chuỗi chính
ADR str2, xâu; Địa chỉ của substring
STMFD (sp) !, {str1, str2, kết quả, link}; Đẩy dây và
BL InStr; phòng cho kết quả. Gọi InStr.
LDMFD (sp) !, {str1, str2, kết quả}; tải dây & dẫn
MOV R0, kết quả; quả trong r0 cho chức năng USR
LDMFD (sp), {} pc!
;
]
TIẾP
REPEAT
INPUT "chuỗi chính 1 ", s1 $," xâu 2 ", s2 $
? mainString = LEN s1 $
substring? = LEN s2 $
$ (mainString + 1) = s1 $
$ (chuỗi con + 1) = s2 $
pos = USR testInstr
IN" INSTR ( "" "s1 $" "" "" "s2 $" "") = "; pos;
IN" ( "; INSTR (s1 $, s2 $)") "
ĐẾN FALSE
Các Lưu ý trong các ý kiến là hoạt động như một lời nhắc nhở về cách thức mà nhiều thanh ghi này được lưu trữ. STM luôn luôn tiết kiệm được đăng ký thấp hơn số trong bộ nhớ trước những số cao hơn. Vì vậy, nếu đặt đúng trên stack là phải có, đăng ký str2 được thấp hơn str1 , mà phải thấp hơn kết quả . Tất nhiên, nếu điều này là không đúng sự thật, chính xác đặt trên stack có thể vẫn đạt được bằng cách đẩy và kéo một thanh ghi tại một thời điểm.
6.4 Số nguyên
Việc lưu trữ và thao tác các số đến cao trên danh sách những thứ mà máy tính là tốt. Đối với hầu hết các mục đích, nguyên (như trái ngược với quan điểm hoặc 'thực tế' nổi) số là đủ, và chúng ta sẽ thảo luận về đại diện và hoạt động của họ trên chúng trong phần này.
Số nguyên đến trong độ rộng khác nhau. Khi ARM là một máy tính 32-bit, và nhóm một hướng dẫn hoạt động trên các toán hạng 32-bit, kích thước thuận tiện nhất rõ ràng là 32-bit. Khi hiểu là số lượng chữ ký, số nguyên 32-bit đại diện cho một loạt các -2147483648 đến 2147483647. số nguyên không dấu cho một phạm vi tương ứng từ 0 đến 4294967295.
Khi được lưu trữ trong bộ nhớ, số nguyên thường được đặt trên ranh giới từ. Điều này cho phép họ được nạp và được lưu trữ trong một hoạt động đơn lẻ. Non số nguyên từ canh đòi hỏi hai LDR s hoặc STR s để di chuyển chúng vào và ra khỏi bộ vi xử lý, thêm vào một số hoạt động masking để 'tham gia lên các bit'.
Nó là hơi lãng phí bộ nhớ để sử dụng bốn byte để lưu trữ số lượng mà chỉ cần một hoặc hai byte. Chúng ta đã thấy rằng các nhân vật sử dụng byte duy nhất để giữ một mã ASCII tám-bit, và độ dài chuỗi lên đến 255 ký tự có thể được lưu trữ trong một byte duy nhất. Một ví dụ về số lượng hai byte là số dòng BASIC (có thể là trong khoảng 0..65279 và do đó yêu cầu 16 bit).
LDRB và STRB phép byte unsigned để để chuyển giữa ARM và bộ nhớ hiệu quả. Có thể có trường hợp, tuy nhiên, khi bạn muốn lưu trữ một số lượng đã ký trong một byte duy nhất, tức là -128 đến 127, thay vì thông thường hơn 0..255. Bây giờ LDRB thực hiện một phần mở rộng không trên các byte, tức là bit 8..31 của các điểm đến được thiết lập là 0 tự động. Điều này có nghĩa rằng khi nạp, một byte ký sẽ có phạm vi của nó thay đổi để 0..255. Để đăng ký mở rộng một byte nạp từ bộ nhớ, bảo quản nhiều ký của mình, trình tự này có thể được sử dụng:
LDRB R0, <address>; Nạp byte
MOV R0, R0, LSL # 24; Di chuyển đến bit 24..31
MOV R0, R0, ASR # 24; Di chuyển trở lại với dấu
Nó hoạt động bằng cách chuyển các byte byte quan trọng nhất của việc đăng ký, vì vậy mà các bit dấu hiệu của byte (bit 7) là bit dấu của từ (bit 31). Sự thay đổi số học ngay sau đó di chuyển các byte trở lại một lần nữa, mở rộng các dấu hiệu như nó không như vậy. Sau này, hướng dẫn ARM 32-bit thông thường có thể được thực hiện trên các từ.
(Nếu bạn đang hoài nghi về kỹ thuật này cho kết quả ký kết đúng, hãy xem xét tám-bit và đại diện bổ sung 32-bit hai của các con số. Nếu bạn kiểm tra một số âm, số không và một số tích cực, bạn sẽ thấy rằng trong mọi trường hợp, bit 7 phiên bản tám-bit là giống như bit 8..31 của đại diện 32-bit).
Các hoạt động cửa hàng không cần bất kỳ sự chú ý đặc biệt: STRB sẽ chỉ cửa hàng bit 0..7 của từ, và bit 7 sẽ là bit dấu (giả định, tất nhiên, rằng số 32-bit đã ký được lưu trữ là trong phạm vi -128 .. + 127 mà một byte duy nhất có thể đại diện).
Double-byte (16-bit) toán hạng được truy cập tốt nhất sử dụng một vài LDRB s hoặc STRB s. Để tải một toán hạng 16-bit unsigned từ việc sử dụng địa chỉ byte canh:
LDRB R0, <address>
LDRB R1, <address> 1
Orr R0, R0, R1, LSL # 8
Việc tính toán <address> 1 có thể yêu cầu một lệnh thêm, nhưng nếu địa chỉ của giá trị hai byte được lưu trữ trong một cơ sở đăng ký, trước hoặc sau lập chỉ mục với một ngay lập tức bù đắp có thể được sử dụng:
LDRB R0, [addr, # 0]
LDRB R1, [addr, # 1]
ORR R0, R0, R1, LSL # 8
Mở rộng các dấu hiệu của một giá trị hai byte tương tự như phương pháp đưa ra cho byte duy nhất hiển thị ở trên, nhưng những thay đổi chỉ là 16 bit.
Để lưu trữ một số lượng mười sáu-bit tại một vị trí byte tùy ý cũng đòi hỏi ba hướng dẫn:
STRB R0, <address>
MOV R0, R0, ROR # 8
STRB R0, <address> 1
Chúng tôi sử dụng ROR # 8 để có được bit 8..15 trong byte thấp của R0. Các số sau đó có thể được phục hồi nếu sử dụng cần thiết:
MOV R0, R0, ROR # 24
Phép nhân và chia
Các phép toán trên số nguyên rất nhiều và đa dạng. Các nhóm một hướng dẫn bao gồm một bộ tốt của họ, nhưng một thiếu sót rõ ràng là phân chia. Ngoài ra, mặc dù có một MUL hướng dẫn, nó được giới hạn kết quả mà phù hợp trong một thanh ghi 32-bit duy nhất. Đôi khi một "độ chính xác kép 'nhân, với một kết quả 64-bit, là cần thiết.
Dưới đây chúng tôi trình bày một thói quen nhân 64-bit và một thủ tục phân chia. Thứ nhất, mặc dù, chúng ta hãy nhìn vào trường hợp đặc biệt của nhân một đăng ký bởi một hằng số. Có một số trường hợp đơn giản chúng ta có thể nhận ra ngay lập tức. Phép nhân với một sức mạnh của hai chỉ đơn giản là vấn đề của việc chuyển đổi đăng ký lại bởi đó số nơi. Ví dụ, để có được R0 * 16, chúng tôi sẽ sử dụng:
MOV R0, R0, ASL # 4
16 = 2 4 . Điều này sẽ chỉ làm việc tốt cho một số tiêu cực như là một tích cực một, miễn là kết quả có thể được đại diện trong bổ sung 32-bit hai của. Phép nhân 2 n -1 hoặc 2 n 1 chỉ là đơn giản:
RSB R0, R0, R0, ASL #n; R0 = R0 * (2 ^ n-1)
ADD R0, R0, R0, ASL #n; R0 = R0 * (2 ^ n + 1)
Vì vậy, để nhân R0 bằng 31 (= 2 5 -1) và một lần nữa bởi 5 (= 2 2 1), chúng tôi sẽ sử dụng:
RSB R0, R0, R0, ASL # 5
Thanh R0, R0, R0, ASL # 2
số khác có thể thu được bằng cách factorising số nhân và thực hiện một số hoạt động thay đổi. Ví dụ, để nhân với 10 chúng ta sẽ nhân 2 sau đó 5:
MOV R0, R0, R0, ASL # 1
Thanh R0, R0, R0, ASL # 2
Bạn thường có thể nhận qua sự kiểm tra trình tự tối ưu của hướng dẫn chuyển đổi sang nhân với một hằng số nhỏ.
Bây giờ chúng tôi trình bày một thói quen làm tăng gấp bội một đăng ký bằng cách khác và tạo ra một kết quả 64-bit trong hai thanh ghi khác. Việc đăng ký LHS và RHS là hai toán hạng nguồn và đích và dest + 1 là các thanh ghi đích. Chúng tôi cũng yêu cầu đăng ký tmp để lưu trữ kết quả tạm thời.
Các công tác thường xuyên bằng cách chia công việc thành bốn nhân lên riêng biệt. Các con số lớn nhất mà MUL có thể xử lý mà không tràn hai toán hạng 16-bit. Vì vậy, nếu chúng ta tách từng ghi 32-bit của chúng tôi thành hai nửa, chúng ta phải thực hiện: -
LHS (thấp) * RHS (thấp)
LHS (thấp) * RHS (cao)
LHS (cao) * RHS (thấp)
LHS (cao) * RHS (Cao)
Bốn sản phẩm sau đó phải được kết hợp trong cách chính xác để tạo ra kết quả cuối cùng. Dưới đây là thói quen, với nhờ Acorn cho phép để tái tạo nó.
;
; 32 X 32 bit nhân.
; Toán hạng nguồn trong LHS, RHS
; dẫn đến đích, đích + 1
; tmp là một đăng ký làm việc
;
.mul64
MOV tmp, LHS, LSR # 16; Nhận top 16 bit của LHS
MOV dest + 1, RHS, LSR # 16; Nhận top 16 bit của RHS
LHS BIC, LHS, tmp, LSL # 16 : xóa top 16 bit của LHS
BIC RHS, RHS, dest + 1, LSL # 16; Xoá top 16 bit của RHS
MUL đích, LHS, RHS; bits 0-15 và 16-31
RHS MUL, tmp, RHS; bits 16 -47, phần 1
MUL LHS, dest + 1, LHS; bits 16-47, phần 2
MUL đÍCH + 1, tmp, dest + 1; bits 32-63
ADDS LHS, RHS, LHS; Thêm hai bit 16-47
ADDCS đÍCH + 1, dest + 1, # & 10000; Thêm vào carry từ trên
ADDS đích, đích, LHS, LSL # 16; cuối cùng dưới 32 bit
ADC đÍCH + 1, dest + 1, LHS, LSR # 16; chung kết top 32 bit
Thời gian tồi tệ nhất trong bốn MUL s là 8 s-chu kỳ mỗi. Điều này dẫn đến một thời gian thức chung trường hợp xấu nhất của 40 s-chu kỳ cho toàn bộ thói quen, hoặc 5us trên một 8MHz ARM.
Những thói quen phân chia chúng tôi đưa ra là một 32-bit 32-bit đã ký chia, để lại một kết quả 32-bit và 32-bit còn lại. Nó sử dụng một thói quen chia unsigned để làm hầu hết công việc. Các thuật toán cho sự phân chia unsigned hoạt động như sau. Số thương ( div ) và còn lại ( mod ) được thiết lập để không, và một số khởi tạo vào 32. LHS được chuyển cho đến khi 1 bit đầu tiên của nó chiếm chút 31, hoặc số lượng đạt đến số không. Trong trường hợp sau, LHS là không, vì vậy trở về thói quen ngay lập tức.
Đối với các lần lặp còn lại, sau đây xảy ra. Các bit đầu của LHS được chuyển vào phía dưới của mod . Điều này tạo nên một giá trị mà từ đó một 'trừ xét xử' của RHS được thực hiện. Nếu trừ đi này sẽ mang lại một kết quả tiêu cực, mod là quá nhỏ, vì vậy các bit tiếp theo của LHS được chuyển vào và một 0 được chuyển vào thương. Nếu không, các phép trừ được thực hiện, và phần còn lại từ trái này trong mod , và 1 được chuyển vào thương. Khi đếm được sử dụng hết, số còn lại từ các bộ phận sẽ được để lại trong mod , và các thương sẽ được trong div .
Trong các thói quen đã ký, dấu của kết quả là sản phẩm của các dấu chỉ của toán hạng (tức là cộng thêm cho cùng một dấu hiệu, trừ cho nhau) và các dấu hiệu của thời gian còn lại là dấu hiệu của phía bên tay trái. Điều này đảm bảo rằng phần còn lại luôn luôn tuân theo công thức:
MOD b = a - b * (một DIV b)
Những thói quen được liệt kê dưới đây:
Org DIM 200
LHS = 0
RHS = 1
div = 2
mod = 3
divSgn = 4
modSgn = 5
count = 6
sp = 13
link = 14
CHO qua = 0 TO 2 Bước 2
P% = org
[opt qua
;
; sDiv32. 32/32 bit đã ký bộ phận / còn lại
; Lập luận trong LHS và RHS. Sử dụng các thanh ghi sau:
; divSgn, modSgn - Các dấu hiệu của các kết quả
; đếm - bit đếm cho vòng lặp chính
; div - giữ LHS / RHS xuất cảnh, kết quả cắt ngắn
; mod - giữ LHS RHS mod xuất cảnh
;
.sDiv32
STMFD (sp ) !, {link}
EORS divSgn, LHS, RHS; Nhận dấu hiệu của div
MOVS modSgn, LHS và các mod
LHS RSBMI, LHS, # 0; Hãy tích cực
RHS TEQ, # 0; Hãy RHS dương
RHS RSBMI, RHS, # 0
BL uDiv32; làm unsigned div
TEQ divSgn, # 0; Bắt dấu hiệu đúng
RSBMI div, div, # 0
TEQ modSgn, # 0; và các mod
mod RSBMI, mod, # 0
;
; Đây chỉ là như vậy chương trình BASIC có thể
; đọc kết quả sau khi cuộc gọi
;
ADR đếm, kết quả
đếm STMIA, {div, mod}
LDMFD (sp), {} pc;! Return
;
.uDiv32
TEQ RHS, # 0; Trap div bởi zero
BEQ divErr
MOV mod, # 0; Init còn lại
MOV div, # 0; và kết quả
đếm MOV, # 32; Thiết lập số
.divLp1
SUBS đếm, đếm, # 1; Nhận 1 bit đầu tiên của LHS
MOVEQ pc, liên kết; thành chút 31. Trở nếu 0
MOVS LHS, LHS, ASL # 1
BPL divLp1
.divLp2
MOVS LHS, LHS, ASL # 1; Nhận bit tiếp theo vào ...
ADC mod, mod, mod, mod để xét xử trừ
CMP mod, RHS;? chúng ta có thể trừ
SUBCS mod, mod, RHS; Vâng, vậy làm
ADC div, div, div, phím Shift mang vào kết quả
đếm SUBS, đếm, # 1; loop Tiếp
BNE divLp2
.divErr
MOV pc, liên kết; Return
;
.result
EQUD 0
EQUD 0
]
TIẾP vượt qua
@ % = & 0A0A
CHO i% = 1 TO 6
A% = RND: B% = RND
CALL sDiv32
d% = kết quả: m% = kết quả 4!
PRINTA% "DIV"; B% "="; d% "(" ; A% DIV B% ")"
PRINTA% "MOD"; B% "="; m% "("; A% B MOD% ")"
IN
TIẾP i%
ASCII để chuyển đổi nhị phân
Các số được biểu diễn dưới dạng các ký tự in vì lợi ích của con người chúng ta, và được lưu trữ trong hệ nhị phân cho hiệu quả trong máy tính. Rõ ràng thói quen cần thiết để chuyển đổi giữa các cơ quan đại diện. Hai chương trình con được liệt kê trong phần này thực hiện chuyển đổi một chuỗi ASCII của các chữ số thập phân với 32-bit đã ký nhị phân, và ngược lại.
Những thói quen ASCII-to-nhị phân có một con trỏ đến một chuỗi và trả về số đại diện bởi các chuỗi, với con trỏ trỏ vào chữ số phi thập phân đầu tiên.
Org DIM 200
REM tập ký
bin = 0
sgn = 1
ptr = 3
ch = 4
sp = 13
link = 14
cr = & 0D
CHO qua = 0 TO 2 Bước 2
P% = org
[lựa chọn không vượt qua
.testAscToBin
; thường xuyên kiểm tra cho ascToBin
;
! STMFD (sp), {link}; Lưu lại địa chỉ
ADR ptr, chữ số; Thiết lập con trỏ đến chuỗi
ascToBin BL; Chuyển nó sang nhị phân trong R0
! LDMFD (sp), {PC}; Return với kết quả
;
.digits
EQUS "-123.456"
EQUB cr
;
; ascToBin. Đọc một chuỗi các chữ số ASCII ở ptr,
; tùy chọn đầu bằng dấu + hoặc - dấu. Trả lại
; ký số nhị phân tương ứng này vào thùng.
;
.ascToBin
! STMFD (sp), {sgn, ch, link}
MOV bin, # 0; Init kết quả
MOV sgn, # 0;. Init dấu để pos
LDRB ch, [ptr, # 0]; Get thể + hoặc -
CMP ch, # ASC "+"; Nếu +, chỉ cần bỏ
BEQ ascSkp
CMP ch, # ASC "-"; Nếu -, phủ nhận dấu hiệu và bỏ qua
MVNEQ sgn, # 0
. ascSkp
ADDEQ ptr, ptr, # 1; Inc ptr nếu + hoặc -
.ascLp
LDRB ch, [ptr, # 0]; đọc chữ số
SUB ch, ch, # ASC "0"; Chuyển đổi sang nhị phân
ch CMP, # 9; Hãy chắc chắn nó là một chữ số
Ascend BHI; Nếu không, kết thúc
ADD bin, bin, bin; Nhận bin * 10. bin bin = * 2
Thanh bin, bin, bin, ASL # 2; bin = bin * 5
Thanh bin, bin, ch; Thêm vào này chữ số
ptr Thanh, ptr, # 1; Tiếp theo nhân vật
B ascLp
.ascEnd
TEQ sgn, # 0; Nếu có - đăng
RSBMI bin, bin, # 0; phủ nhận kết quả
LDMFD (sp), {sgn, ch, pc}!
]
TIẾP qua
PRINT "những nên in như nhau:"
iN $ chữ số '; USRtestAscToBin
Chú ý rằng chúng ta không sử dụng một mục đích chung nhân để có được bin * 10. Vì đây là bin * 2 * 5, chúng tôi có thể có được kết quả mong muốn sử dụng chỉ cần một vài Thanh s. Cũng như nhiều các thói quen trong cuốn sách này, ví dụ trên minh họa một kỹ thuật hơn là cung cấp một giải pháp đầy đủ chính thức. Nó có thể được cải thiện trong một vài cách khác nhau, ví dụ như bắt tình hình có số lượng quá lớn, hoặc không có chữ số được đọc ở tất cả.
Để chuyển đổi một số từ nhị phân thành một chuỗi ký tự ASCII, chúng ta có thể sử dụng sự phân chia phổ biến và phương pháp còn lại. Ở mỗi giai đoạn con số này chia cho 10 còn lại sau khi chia là chữ số tiếp theo để in, và điều này được lặp đi lặp lại cho đến khi thương số là số không.
Sử dụng phương pháp này, các chữ số được lấy từ bên phải, tức là các chữ số đáng kể nhất là tính toán đầu tiên. Nói chung, chúng tôi muốn chúng theo thứ tự ngược lại - các chữ số quan trọng nhất đầu tiên.Để đảo ngược thứ tự của các chữ số, họ được đẩy vào stack khi họ thu được. Khi chuyển đổi hoàn tất, họ được kéo ra khỏi stack. Bởi vì, lần đầu ra cuối cùng trong 'tài sản của chồng, chữ số cuối cùng đẩy (tận cùng bên trái một) là một trong những đầu tiên kéo trở lại.
buffSize = 12
DIM org 200, đệm buffSize
phân bổ REM ký
bin = 0
ptr = 1
sgn = 2
LHS = 3
RHS = 4
div = 5
mod = 6
count = 7
len = 8
sp = 13
link = 14
cr = & 0D
CHO qua = 0 TO 2 Bước 2
P% = org
[opt qua
;
; binToAscii - chuyển đổi bổ sung 32-bit hai của
; số thành một chuỗi ASCII.
; Mở nhập cảnh, ptr giữ địa chỉ của một bộ đệm
; khu vực trong đó ASCII là được lưu trữ.
; bin chứa các số nhị phân.
; Mở lối ra, ptr điểm để các chữ số đầu tiên (hoặc -
; dấu hiệu) của chuỗi ASCII. bin = 0
;
.binToAscii
STMFD (sp), {ptr, sgn, LHS, RHS, div, mod, link}!
MOV len, # 0; số Init của các chữ số
MOV mod, # ASC "-"
TEQ bin, # 0 ; Nếu-ve, ký lục và phủ nhận
STRMIB mod, [ptr], # 1
RSBMI bin, bin, # 0
.b2aLp
MOV LHS, bin; Nhận LHS và RHS cho uDiv32
MOV RHS, # 10
BL uDiv32; Nhận chữ số trong mod , phần còn lại trong div
mod Thanh, mod, # ASC "0"; Chuyển đổi chữ số để ASCII
STMFD (sp), {mod};! Lưu chữ số trên stack
ADD len, len, # 1; Inc dài chuỗi
MOVS bin, div; Nếu bất kỳ hơn, có được chữ số tiếp theo
BNE b2aLp
;
.b2aLp2
LDMFD (sp), {mod};! lấy một chữ số
mod STRB, [ptr], # 1; cửa hàng nó trong chuỗi
SUBS len, len, # 1; Decrement đếm
BNE b2aLp2
MOV mod, # cr; Kết thúc bằng một CR
mod STRB, [ptr], # 1
LDMFD (sp), {ptr, sgn, LHS, RHS, div, mod, pc}!
;
;
.uDiv32
STMFD (sp) !, {đếm, link}
RHS TEQ, # 0; Trap div bởi zero
BEQ divErr
MOV mod, # 0; Init còn lại
MOV div, # 0; và kết quả
MOV đếm, # 32; Thiết lập số
.divLp1
SUBS đếm, đếm , # 1; Nhận 1 bit đầu tiên của LHS
MOVEQ pc, liên kết; thành chút 31. Trở nếu 0
MOVS LHS, LHS, ASL # 1
BPL divLp1
.divLp2
MOVS LHS, LHS, ASL # 1; Nhận bit tiếp vào ...
ADC mod, mod, mod, mod để xét xử trừ
CMP mod, RHS, chúng ta có thể trừ đi?
SUBCS mod, mod, RHS; Vâng, vậy làm
ADC div, div, div, phím Shift mang vào kết quả
đếm SUBS, đếm, # 1; vòng lặp tiếp theo
BNE divLp2
.divErr
! LDMFD (sp), {count, pc}
]
TIẾP vượt qua
A% = - 12345678
B% = đệm
CALL binToAscii
IN "những nên được như vậy:"
IN; A '$ đệm%
Vì không có cách nhanh chóng làm một chia cho 10, chúng tôi sử dụng các uDiv32 thói quen cho trước đó, với LHS và RHS thiết lập một cách thích hợp.
6,5 điểm nổi
Nhiều số lượng thực tế cuộc sống không thể được lưu trữ một cách chính xác trong số nguyên. số lượng như vậy có phần phân đoạn, bị mất trong các đại diện số nguyên, hoặc chỉ đơn giản là quá lớn về độ lớn được lưu trữ trong một số nguyên của 32 (hoặc thậm chí 64) bit.
Đại diện dấu chấm động được sử dụng để khắc phục những hạn chế của các số nguyên. Floating point, hoặc FP, con số này được thể hiện trong ASCII như, ví dụ, 1,23, trong đó có một phần phân đoạn của 0,23, hoặc 2.345E6, trong đó có một phần phân đoạn và một số mũ. Số mũ, số sau của E, là sức mạnh của mười mà theo đó các phần khác (2,345 trong ví dụ này) phải được nhân rộng để có được số lượng mong muốn. Các 'một phần khác được gọi là mantissa. Trong ví dụ này, số lượng là 2,345 * 10 6 hoặc 2.345.000.
Trong hệ nhị phân, số điểm nổi cũng được chia thành các mantissa và số mũ. Có nhiều định dạng của số dấu chấm động. Ví dụ, kích thước của mantissa, mà quyết định có bao nhiêu chữ số có thể được lưu trữ một cách chính xác, và kích thước của số mũ, xác định phạm vi của độ lớn có thể được biểu diễn, cả hai thay đổi.
Các phép toán trên số điểm nổi có xu hướng khá tham gia. Thậm chí bổ sung đơn giản cần một vài bước. Vì lý do này, nó thường chỉ là hiệu quả để viết trong một ngôn ngữ cấp cao khi nhiều tính toán FP được thực hiện, và các lợi thế của việc sử dụng lắp ráp có phần giảm sút. Ngoài ra, hầu hết các máy cung cấp một thư viện các thói quen điểm đó là có sẵn cho các chương trình hợp ngữ nổi, vì vậy có rất ít điểm trong việc nhân bản chúng ở đây.
Chúng tôi sẽ, tuy nhiên, mô tả một định dạng dấu chấm động điển hình. Đặc biệt, cách thức mà BBC BASIC lưu trữ các giá trị dấu chấm động của nó được mô tả.
Một số FP trong BBC BASIC được biểu diễn như năm byte. Bốn byte mantissa, và các chứa các chữ số đáng kể về số lượng. Mantissa có điểm nhị phân tưởng tượng ngay trước khi bit quan trọng nhất của nó. Điều này đóng vai trò như một điểm thập phân, và chữ số sau dấu đại diện cho quyền lực tiêu cực liên tiếp của 2. Ví dụ, số 0,101 đại diện cho 1/2 + 0/4 + 1/8 hoặc 5/8 hoặc 0.625 trong số thập phân.
Khi lưu trữ, số FP là ở dạng bình thường. Điều này có nghĩa rằng các chữ số ngay sau khi các điểm là 1. Do đó, một bình thường hóa 32-bit mantissa có thể đại diện cho các số trong phạm vi:
0,10000000000000000000000000000000 để
0,11111111111111111111111111111111
trong hệ nhị phân là 0,5-0,9999999998 trong hệ thập phân.
Đại diện cho số ngoài phạm vi này, một byte số mũ duy nhất được sử dụng. Đây có thể được xem như một số thay đổi. Nó cung cấp cho một số của bao nhiêu nơi điểm nên chuyển sang bên phải để có được giá trị mong muốn. Ví dụ, để đại diện cho 1,5 trong dấu chấm động nhị phân, chúng ta sẽ bắt đầu với giá trị nhị phân 1.1, tức là 1 + 1/2. Ở dạng bình thường, điều này là 0,11. Để có được giá trị ban đầu, chúng tôi phải di chuyển điểm một nơi bên phải. Vì vậy, các số mũ là 1.
Chúng ta phải có khả năng đại diện cho di chuyển sang trái của điểm quá, vì vậy mà số lượng nhỏ hơn 0.5 có thể được đại diện. số mũ âm tượng trưng cho trái thay đổi của điểm. Ví dụ, nhị phân là 0,25 (tức là một phần tư) là 0,01. Ở dạng bình thường này là 0,1. Để có được điều này, các điểm được di chuyển một nơi bên trái, vì số mũ là -1.
Hai của bổ sung có thể được sử dụng để đại diện cho số mũ như một số ký, nhưng là bình thường hơn để sử dụng một định dạng dư thừa-128. Trong định dạng này, 128 được bổ sung vào số mũ thực tế. Vì vậy, nếu các số mũ là không, đại diện không có sự thay đổi của điểm từ hình thức bình thường, nó sẽ được lưu trữ như là 128 + 0, hay chỉ là 128. Một số mũ âm, ví dụ -2, sẽ được lưu trữ như là 128-2, hoặc 126 .
Sử dụng phương pháp dư thừa-128, chúng tôi có thể đại diện cho số mũ trong khoảng -128 (số mũ được lưu trữ như là số không) để 127 (số mũ lưu trữ như là 255). Như vậy độ lớn nhỏ nhất chúng tôi có thể đại diện là 0,5 / (2 128 ), hoặc 1.46936794E-39. Số lượng lớn nhất 0,9999999998 * (2 127 ), hoặc 1.701411834E38
Cho đến nay, chúng tôi đã không được đề cập mantissas tiêu cực. Rõ ràng là chúng ta cần phải đại diện cho các số âm cũng như những người tích cực. Một 'lừa' thông thường, và một trong đó BBC BASIC sử dụng, là giả định rằng các bit quan trọng nhất là 1 (như con số này luôn ở dạng bình thường) và sử dụng vị trí bit để lưu trữ các bit dấu: một số không cho các số dương, và 1 cho số âm.
Chúng ta có thể tóm tắt đại diện điểm nổi bằng cách xem xét các nội dung trong năm byte được sử dụng để lưu trữ chúng trong bộ nhớ.
byte 0LS byte của mantissa
byte 1Thứ hai LSB của mantissa
byte 2Thứ hai MSB của mantissa
byte 3MS byte của mantissa. điểm nhị phân chỉ bên trái của bit 7
byte 4Số mũ, dư thừa-128 hình thức
Hãy xem xét các số 1.032,45. Đầu tiên, chúng ta thấy các số mũ, tức là theo những gì sức mạnh của hai số phải được phân chia để có được một kết quả giữa 0,5 và 0,9999999. Đây là 11, như 1032,45 / (2 11 ) = 0,504125976. Mantissa, trong hệ nhị phân, là: 0.10000001 00001110 01100110 01100110 hoặc, trong hex 81 0E 66 66. Vì vậy, chúng tôi sẽ lưu trữ số như:
byte 0LSB = & 66
byte 12rd LSB = & 66
byte 22 MSB = & 0E
byte 3MSB = & 81 VÀ & 7F = & 01
byte 4số mũ = 11 + 128 = & 8B
Đây là năm byte bạn sẽ thấy nếu bạn thực hiện như sau trong BASIC:
Val DIM 4: REM Nhận năm byte
| val = 1032,45: REM Poke các giá trị điểm nổi
CHO i = 0 TO 4: REM In năm byte
IN ~ val i?
TIẾP i
Sau khi mô tả định dạng dấu chấm động BBC BASIC trong một số chi tiết, bây giờ chúng tôi phải thú nhận rằng nó không phải là giống như được sử dụng bởi các hướng dẫn điểm ARM nổi. Đó là, tuy nhiên, dễ dàng nhất để 'chơi' với và hiểu được.
Các hướng dẫn điểm ARM nổi là phần mở rộng cho bộ mô tả trong Chương Ba. Họ thực hiện theo các tiêu chuẩn IEEE cho dấu chấm động. Việc thực hiện các hướng dẫn ban đầu của phần mềm mô phỏng, nhưng cuối cùng một đơn vị phần cứng nhanh hơn nhiều sẽ có sẵn để thực hiện chúng. ARM FP bộ hướng dẫn đầy đủ và định dạng được mô tả trong Phụ lục B.
6.6 loại có cấu trúc
Đôi khi, chúng tôi muốn để đối phó với một nhóm các giá trị thay vì các mặt hàng chỉ duy nhất. Chúng ta đã thấy một ví dụ về điều này - chuỗi các nhóm, hoặc các mảng, các ký tự. khối tham số cũng có thể được coi là một loại cấu trúc. Những tương ứng với các bản ghi trong Pascal, hoặc cấu trúc trong C.
vấn đề cơ bản mảng
Chúng ta định nghĩa một mảng như là một chuỗi các đối tượng cùng loại mà có thể được truy cập riêng. Một chỉ số hoặc subscript được sử dụng để biểu thị mà mục trong một mảng là quan tâm đến chúng tôi.Bạn đã có thể đi qua các mảng trong BASIC. Tuyên bố:
giá trị DIM% (100)
phân bổ không gian cho 101 số nguyên, được gọi là giá trị% (0) để đánh giá% (100) . Các số trong ngoặc là số dưới. Trong assembler, chúng tôi sử dụng một kỹ thuật tương tự. Trong một đăng ký, chúng tôi giữ địa chỉ cơ sở của mảng. Đây là địa chỉ của mục đầu tiên. Trong ghi khác là chỉ số. ARM cung cấp hai hoạt động trên mặt mảng: bạn có thể tải nó vào trong bộ vi xử lý, hoặc lưu trữ một trong bộ nhớ từ bộ vi xử lý.
Hãy xem xét một ví dụ cụ thể. Giả sử ghi R0 chứa địa chỉ cơ sở của một mảng các số nguyên bốn byte, và R1 chứa subscript của một trong chúng tôi muốn tải. Hướng dẫn này sẽ được sử dụng:
LDR R2, [R0, R1, LSL # 2]
Lưu ý rằng khi R1 giữ chỉ số, hoặc phụ, các nguyên tố này, chúng ta cần phải nhân này bằng bốn (bằng cách sử dụng LSL # 2 ) để có được những thực tế bù đắp. Điều này sau đó sẽ được thêm vào địa chỉ cơ sở trong R0 và lời từ địa chỉ này được nạp vào R2. Không có ! Lúc kết thúc, vì vậy R0 không bị ảnh hưởng bởi ghi lại.
Nếu mảng là đối tượng byte kích thước, các hoạt động tải tương ứng sẽ là:
LDRB R2, [R0, R1]
Lần này không được nhân rộng của chỉ số, như từng mục trong mảng chỉ chiếm một byte.
Nếu bạn đang truy cập vào một mảng các đối tượng đó là một số kích thước bất tiện, bạn sẽ cần phải quy mô chỉ số vào một byte trước khi tải tài liệu. Hơn nữa, điều chỉnh hơn nữa có thể là cần thiết để đảm bảo rằng các tải trọng diễn ra trên ranh giới từ.
Để minh họa cho vấn đề tải vật kỳ lạ có kích thước từ sự sắp xếp tùy ý, chúng tôi đưa ra một thói quen dưới đây để tải một năm byte giá trị dấu chấm vào hai thanh ghi, mant (mantissa) và exp (mũ). Con số này được lưu trữ trong bộ nhớ như một mantissa bốn byte tiếp theo là một số mũ single-byte. Một mảng của các đối tượng này có thể sử dụng mỗi hai từ, từ đầu tiên giữ mantissa và LSB của từ thứ hai lưu trữ mantissa. Sau đó nó sẽ là một công việc đơn giản để tải hai từ từ một địa chỉ word-aligned, và mặt nạ ra phần chưa sử dụng của chữ số mũ.
Sử dụng cả hai từ để lưu trữ năm byte là lãng phí khi nhiều yếu tố được sử dụng (ví dụ như một mảng của 5000 con số sẽ lãng phí 15.000 byte), vì vậy chúng tôi rõ ràng là có để lưu trữ số liên tục kế nhau. Nó là rất có thể, do đó, rằng mantissa và số mũ sẽ được sắp xếp theo một cách mà làm cho đơn giản LDR hướng dẫn đủ để nạp số vào sổ đăng ký.
Hãy xem xét các giá trị được lưu trữ bắt đầu từ địa chỉ & 4001:
& 4000*****************
& 4001LSB của mantissa
& 40022 LSB của mantissa
& 40032 MSB của mantissa
& 4004MSB của mantissa
& 4005exponent
& 4006*****************
& 4007*****************
Ba byte số được tổ chức trong ba byte quan trọng nhất của một từ; hai byte cuối cùng được lưu trữ vào lúc bắt đầu của từ tiếp theo.
Các kỹ thuật chúng tôi sẽ sử dụng là để nạp hai từ đó giá trị nằm giữa, sau đó chuyển các thanh ghi thích hợp để mant chứa bốn byte của mantissa theo đúng thứ tự, và LSB của exp chứa các byte số mũ.
Nhập cảnh sang mã, cơ sở chứa địa chỉ cơ sở của mảng, và tắt giữ chỉ số (trong các yếu tố chứ không phải là byte).
ADD tắt, tắt, tắt, LSL # 2; offset = 5 * bù đắp
ADD cơ sở, cơ sở, tắt; cơ sở = base + 5 * n
và tắt, cơ sở, # 3; Bắt bù đắp trong byte
cơ sở BIC, cơ sở, # 3; Nhận thấp từ địa chỉ
cơ sở LDMIA, {mant, exp}; Load hai từ
MOVS tắt, tắt, LSL # 3; Bắt bù đắp bằng bit
mant MOVNE, mant, LSR tắt; phím Shift mantissa đúng
RSBNE cơ sở, tắt, # 32; Nhận chuyển cho số mũ
mant ORRNE, mant, điểm kinh nghiệm, cơ sở LSL; OR trong mantissa
.part
MOVNE exp, exp, LSR tắt; Nhận mũ là LSB
và điểm kinh nghiệm, điểm kinh nghiệm, # & FF; Zero, byte cao của điểm kinh nghiệm
Chú ý chúng tôi sử dụng LDMIA để tải hai từ. Mã này giả định rằng số lượng đăng ký của mant là thấp hơn so với điểm kinh nghiệm , do đó những từ được nạp theo đúng thứ tự.
Bốn huấn cuối cùng là tất cả các điều kiện trên byte là khác không. Nếu nó là không, giá trị đã được trên một ranh giới từ, và không chuyển dịch được yêu cầu.
Mảng các chuỗi
Chúng tôi đã lưu ý rằng một chuỗi là một mảng ký tự. Đôi khi, chúng ta muốn một mảng các chuỗi, tức là một mảng của mảng ký tự. Ví dụ, việc kê khai BASIC:
DIM tên $ (10)
cho chúng ta một mảng của 11 chuỗi, tên $ (0) để tên $ (10) . Làm thế nào để chúng tôi tổ chức như một cấu trúc trong ngôn ngữ lắp ráp? Có hai giải pháp. Nếu mỗi chuỗi là phải có độ dài cố định, cách dễ nhất là để lưu trữ các chuỗi liên tục kế nhau trong bộ nhớ. Giả sử chúng ta muốn mười dây mười kí tự. Điều này rõ ràng sẽ chiếm 100 byte. Nếu địa chỉ cơ sở của các byte được lưu trữ trong cơ sở , và chỉ số của chuỗi chúng ta muốn ở phụ , sau đó mã này sẽ được sử dụng để tính toán các địa chỉ bắt đầu của chuỗi phụ:
ADD cơ sở, cơ sở, tiểu, LSL # 3; Thêm phụ * 8
cơ sở Thanh, cơ sở, tiểu, LSL # 1; Thêm phụ * 2
Sau khi hướng dẫn này, cơ sở sẽ trỏ đến (cũ) cơ sở + 10 * phụ , tức là các nhân vật bắt đầu của chuỗi số phụ .
Lưu trữ tất cả các ký tự của mỗi chuỗi có thể là lãng phí nếu có nhiều dây và họ có thể có độ dài khác nhau rộng rãi. Ví dụ, nếu nhiều các chuỗi trong một mảng không chứa các ký tự cho rất nhiều thời gian, lưu trữ được sử dụng cho họ là lãng phí. Các giải pháp chúng tôi trình bày là sử dụng một mảng các khối thông tin chuỗi thay vì các nhân vật thực tế.
Một khối thông tin chuỗi (SIB) là một cấu trúc trong đó mô tả các địa chỉ và chiều dài của một chuỗi. Không giống như các chuỗi chính nó, nó là một chiều dài cố định, như vậy là dễ dàng cho việc lưu trữ trong một mảng. BASIC sử dụng SIB để mô tả chuỗi của nó. Khi bạn DIM một mảng chuỗi trong BASIC, lưu trữ chỉ được giao là cho số lượng thích hợp của SIB. Không lưu trữ ký tự được phân bổ cho đến khi bạn bắt đầu giao cho các dây.
Định dạng của một BASIC SIB là:
byte 0-3 địa chỉ của chuỗi
byte 4 chiều dài hiện tại của chuỗi
Khi bạn DIM một mảng, tất cả các mục có byte chiều dài của họ thiết lập để không. Ngay sau khi bạn gán một cái gì đó để chuỗi, BASIC phân bổ một số lưu trữ cho nó và điền vào phần địa chỉ. Cách thức mà BASIC phân bổ lưu trữ cho các chuỗi là thú vị ở bên phải của riêng của nó, và được mô tả trong sectionÊ6.7
Để minh họa cách chúng ta có thể sử dụng một mảng của SIB, các thói quen dưới đây có một địa chỉ cơ sở và một số phụ, và in nội dung của chuỗi đó.
DIM org 200
phụ = 0
cơ sở = 1
len = 2
p1 = 3
p2 = 4
argPtr = 9: con trỏ REM BASIC để CALL luận
WriteC = 0
sp = 13
link = 14
CHO qua = 0 TO 2 Bước 2
P% = org
[ opt vượt qua
;
; cơ sở in $ (phụ), nơi các điểm cơ sở để bắt đầu
; một mảng của khối thứ năm-byte của các định dạng:
; 0..3 con trỏ đến chuỗi
; 4 chiều dài của chuỗi (xem phần 4.7)
; và phụ là 0..n subscript của mong muốn
; chuỗi. Các SIB có thể có bất kỳ sự liên kết byte.
;
.pStr
; Nhận địa chỉ của SIB = cơ + sub * 5
cơ sở Thanh, cơ sở, tiểu, LSL # 2; cơ sở = base + sub 4 *
cơ sở Thanh, cơ sở, tiểu; cơ sở = cơ sở + sub * 1
LDRB len, [cơ sở, # 4]; Nhận chuỗi len
TEQ len, # 0; Nếu không, không có gì để làm
MOVEQ pc, liên kết
;
; tải trọng liên kết tùy tiện của con trỏ bốn byte vào
; p1. Địa chỉ của con trỏ ở cơ sở
;
VÀ phụ, cơ sở, # 3; Bắt bù đắp trong byte
cơ sở BIC, cơ sở, # 3; Nhận thấp từ địa chỉ
cơ sở LDMFD, {p1, p2}; Load hai từ
MOVS sub, sub, LSL # 3; Bắt bù đắp bằng bit
MOVNE p1, p1, LSR phụ; phím shift thấp từ
tiểu RSBNE, sub, # 32; Nhận ca cho từ cao
ORRNE p1, p1, p2, LSL phụ; ORR trong từ cao
;
; Bây giờ in chuỗi. NB len> 0 vì vậy chúng tôi có thể kiểm tra tại
; kết thúc vòng lặp
;
.pStrLp
LDRB R0, [p1], # 1; Load một nhân vật vào R0
SWI WriteC; In nó
SUBS len, len, # 1; Decrememt đếm
BNE pStrLp; Vòng nếu có nhiều
.endPStr
MOV pc, liên kết; Return
;
; testPstr. Này có một subscipt trong tiểu (r0)
; và một tham số mảng chuỗi CALL BASIC
, và in ra chuỗi thích hợp
;
.testPstr
STMFD (sp), {link}!
Cơ sở LDR, [argPtr]; Nạp địa chỉ của
BL pStr; parm CALL. In chuỗi
LDMFD (sp)!, {Pc}
]
TIẾP qua
DIM một $ (10)
CHO i% = 0 TO 10
một $ (i%) = STR $ RND
TIẾP i%
CHO i% = 0 TO 10
A% = i%
IN "này nên nói" một $ (i%) ":";
CALL testPstr, một $ (0)
IN
TIẾP i%
mảng đa chiều
Một danh sách duy nhất của các mặt hàng không phải là luôn luôn đủ. Nó có thể là tự nhiên hơn để lưu trữ các mặt hàng như một bảng của hai hoặc thậm chí nhiều kích thước. Một khai báo mảng BASIC của hai kích thước là:
Này phân bổ không gian cho một ma trận của 36 số nguyên:
Chúng ta có thể sử dụng các mảng như vậy trong ngôn ngữ lắp ráp bằng cách tưởng tượng các hàng đặt ra từ đầu đến cuối trong bộ nhớ. Do đó, sáu chữ đầu tiên của mảng sẽ giữ t% (0,0) để t% (0,5) . Tiếp theo sáu sẽ lưu trữ t% (1,0) để t% (1,5) và như vậy. Để tính toán các địa chỉ của t% (a, b) chúng tôi sử dụng cơ sở + a * lim + b , nơi lim là giới hạn của các subscript thứ hai, đó là 6 trong trường hợp này.
Những thói quen dưới đây có cơ sở , Sub1 và sub2 . Nó tính toán các địa chỉ của các yếu tố cần thiết, giả sử mỗi phần tử là 4 byte (ví dụ như một số nguyên) và có 16 nguyên tố trong mỗi hàng.
ADD Sub1, sub2, Sub1, LSL # 4; Sub1 = sub2 + 16 * Sub1
ADD cơ sở, cơ sở, Sub1, LSL # 2; cơ sở = base + Sub1 * 4
Một khi cơ sở đã được tính toán, hướng dẫn thông thường có thể được sử dụng để tải các số nguyên tại địa chỉ đó. Trong trường hợp tổng quát hơn, số lượng các yếu tố trong mỗi hàng sẽ được lưu trữ trong một thanh ghi, và một vị tướng nhân sử dụng để nhân Sub1 giới hạn này.
các lĩnh vực bit
Chúng tôi kết thúc cuộc thảo luận này của các loại cơ bản của dữ liệu bằng cách quay trở lại mức thấp nhất của đại diện: các bit. Chúng ta đã thấy các chuỗi bit, thường là trong bội số của tám, có thể được sử dụng để đại diện cho các ký tự, số nguyên, con trỏ và số dấu chấm động. Tuy nhiên, bit duy nhất có thể hữu ích lưu trữ thông tin quá.
Một chữ số nhị phân có thể đại diện cho hai giá trị: 0 hoặc 1. Thường thì điều này là tất cả chúng ta cần phải phân biệt giữa hai sự kiện. Một bit được sử dụng để đại diện cho một sự lựa chọn như có / không, đúng / sai, thành công / thất bại được gọi là một lá cờ. Chúng ta đã biết một số cờ hữu ích: cờ kết quả vào sổ trạng thái. V cờ ví dụ đại diện tràn / không tràn.
Nó thường được tìm thấy trong nhiều chương trình tập cờ mà có thể được nhóm lại với nhau và được lưu trữ trong một byte đơn hoặc từ. Hãy xem xét một trình soạn thảo văn bản. Có thể có những lá cờ để chỉ chèn / chế độ hưởng hay đè lên, biện minh / không có chế độ biện minh, giúp hiển thị / không có sự giúp đỡ, trường hợp nhạy cảm / tìm kiếm không nhạy cảm. Mỗi trong số này sẽ được chỉ định một bit trong byte cờ. Các giá trị cho một lá cờ là các bit ở vị trí đó. Như vậy chúng ta có thể xác định các ví dụ trên là:
insMode = & 01
juMode = & 02
hlpMode = & 04
snsMode = & 08
Có bốn hoạt động chính trên lá cờ: cài đặt, xóa, nghịch và thử nghiệm. Để thiết lập một lá cờ, Orr hoạt động được sử dụng. Ví dụ, để thiết lập các insMode cờ của thanh ghi cờ :
Orr cờ, cờ, #insMode
Thanh toán bù trừ một lá cờ được thực hiện sử dụng BIC , cắn rõ ràng. Để xóa cả hlpMode và snsMode :
cờ BIC, cờ, #hlpMode HOẶC snsMode
Để đảo ngược một lá cờ chúng tôi sử dụng các EOR hoạt động. Điều này thường được gọi là "chuyển đổi qua một lá cờ ', bởi vì áp dụng các hoạt động lặp đi lặp lại có tác dụng chuyển đổi cờ giữa hai giá trị.
Để đảo ngược juMode cờ:
EOR cờ, cờ, #juMode
Cuối cùng, để kiểm tra trạng thái của một lá cờ, chúng tôi sử dụng TST . Này thực hiện  hoạt động, và kết quả là số không nếu cờ được xóa bỏ, và khác không nếu nó được thiết lập:
TST cờ, #insMode
kiểm tra cờ chế độ chèn. Nếu bạn kiểm tra nhiều hơn một lá cờ trong một đơn TST , kết quả không phải là zero nếu có những lá cờ được thiết lập, và không nếu tất cả chúng đều bị xóa. Bạn cũng có thể sử dụng TEQ để xem nếu tất cả các tập cờ được thiết lập và phần còn lại sẽ bị xóa. Ví dụ,
cờ TEQ, #insMode HOẶC juMode
đặt cờ Z nếu insMode và juMode được thiết lập và hlpMode và snsModer sẽ bị xóa. Nếu Z là xóa.
Như 32 bit được tổ chức trong một từ duy nhất, các mảng của cờ có thể được lưu trữ rất hiệu quả. Để minh họa điều này, chúng tôi hiển thị Byte nổi tiếng Sàng tạp chí của chương trình Eratosthenes. Chuẩn này thường được sử dụng để kiểm tra tốc độ của một vài loại đơn giản hoạt động, ví dụ như khi trình biên dịch khác nhau cho một ngôn ngữ đang được so sánh. Mục đích của chương trình là để tìm số nguyên tố sử dụng một kỹ thuật do các nhà toán học Hy Lạp cùng tên.
Các kỹ thuật sàng hoạt động như sau. Bắt đầu với một mảng của cờ, một cho mỗi số nguyên từ 2 đến thủ tối đa được tìm thấy. Tất cả các lá cờ được thiết lập để 'true' ban đầu, chỉ ra rằng bất kỳ số có thể là một nguyên tố. Đi qua các mảng kiếm bit 'true' bên cạnh. Các số tương ứng với bit này là số nguyên tố, vì vậy đầu ra nó. Sau đó đi qua tất cả các bội số của số này, thiết lập bit của họ để 'giả' để loại bỏ chúng từ các cuộc điều tra. Lặp lại quá trình này cho đến cuối mảng.
phiên bản Byte của các thuật toán sàng là hơi khác nhau khi nó bắt đầu từ giả thiết rằng tất cả các số chẵn (trừ hai) là không chuẩn, do đó, họ thậm chí không có. Để so sánh, chúng tôi cung cấp một phiên bản BBC BASIC của thuật toán đầu tiên, sau đó là một ARM lắp ráp. Các phiên bản BASIC được hiển thị ở trang sau.
Mỗi lá cờ được lưu trữ trong một byte, và các lưu trữ cho các mảng kích thước% byte thu được bằng cách sử dụng DIM tuyên bố. Chú ý rằng chương trình không thực sự in các số nguyên tố phát hiện ra nó, bởi vì các ý tưởng của các điểm chuẩn là để kiểm tra tốc độ của những thứ như mảng truy cập, không in ấn. Các vị trí trong chương trình mà các thủ sẽ được in được thể hiện trong một REM .
kích thước% = 8190
DIM cờ% kích thước%
count% = 0
CHO i% = 0 TO kích thước%
cờ%? i% = TRUE
TIẾP i%
CHO i% = 0 TO kích thước%
NẾU cờ%? i% THEN
Thủ% = i % + i% + 3: REM IN thủ%
k% = thủ% + i%
KHI k% <= kích thước%
? cờ% k% = FALSE
k% + = thủ%
endwhile
đếm% + = 1
ENDIF
TIẾP i% IN đếm%
Trong phiên bản ARM lắp ráp, chúng tôi là tám lần hiệu quả hơn trong việc lưu trữ những lá cờ, và sử dụng một chút duy nhất cho mỗi một. Do đó 32 cờ có thể được lưu trữ trong mỗi từ bộ nhớ. Các phiên bản ARM được hiển thị dưới đây:
Org DIM 2000
phân bổ REM ký
count = 0
ptr = 1
i = 2
mask = 3
cơ sở = 4
thủ = 5
k = 6
tmp = 7
size = 8
iter = 9
link = 14
SIZE = 8190
iterations = 10
CHO qua = 0 TO 2 Bước 2
P% = org
[opt qua
; Sieve of Eratosthenes trong ARM lắp ráp
; các mảng cờ SIZE được lưu trữ 32 mỗi từ từ
; địa chỉ 'theArray'. Các phần tử 0 được lưu trữ ở bit
; 0 của từ 0, yếu tố thứ 32 ở bit 0 của từ 1, và vì vậy
, trên. 'Cơ sở' là từ canh
;
; Đăng ký:
; đếm nắm giữ số lượng các số nguyên tố tìm thấy
; mặt nạ được sử dụng như một mặt nạ bit để cô lập các cờ yêu cầu
; ptr sử dụng như một con trỏ chung / bù đắp vào mảng
; tôi được sử dụng như một thanh ghi đếm
; kích thước giữ SIZE giá trị để so sánh
; cơ sở giữ địa chỉ của sự bắt đầu của mảng
; Thủ giữ số nguyên tố hiện tại
; k giữ mục hiện được 'gạch bỏ'
; tmp là tạm thời
; ITER giữ số lần lặp lại
;
.sieve
MOV ITER, # lặp
.mainLoop
ADR cơ sở, theArray
mvn mặt nạ, # 0; Nhận & FFFFFFFF, tức là tất cả các bit thiết lập
kích thước MOV, # SIZE VÀ & FF; tải kích thước với SIZE trong 2 bước
Orr kích thước , kích thước, # SIZE vÀ & FF00
;
; Khởi tạo mảng cho tất cả 'true'. Đầu tiên lưu
; lời đầy đủ (SIZE DIV 32 của họ), thì các phần
; từ lúc kết thúc
;
MOV i, # DIV SIZE 32; Vòng counter = số từ
MOV ptr, cơ sở; địa chỉ bắt đầu cho initing mảng
.initLp
STR mặt nạ, [ptr], # 4; cửa hàng một từ và cập nhật con trỏ
SUBS i, i, # 1; từ tiếp theo
BNE initLp
LDR tmp, [ptr]; Get cuối cùng, không đầy đủ từ
MOV mặt nạ, mặt nạ, LSR # 32-SIZE MOD 32: xóa bit đầu
ORR tmp, tmp, mặt nạ; Thiết lập các bit dưới
STR tmp, [ptr]; cửa hàng nó trở lại
MOV i, # 0; Init đếm cho chính loop
count MOV, # 0
.lp
MOV ptr, i, LSR # 5; Nhận lời bù đắp cho điều này chút
MOV mặt nạ, # 1; Nhận mặt nạ cho bit này
và tmp, i, # 31; bit không. = I MOD 32
MOV mặt nạ, mặt nạ, LSL tmp
LDR tmp, [cơ sở, ptr, LSL # 2]; Lấy từ
AND tmp, tmp, mặt nạ; Xem nếu bit được thiết lập
BEQ nextLp; Không nên bỏ qua
ADD thủ, i, i; Nhận thủ
ADD nguyên tố, nguyên tố, # 3
Thanh k, i, gốc; Get ban đầu k
đếm Thanh, đếm, # 1; Tăng đếm
.while
CMP k, kích thước; Trong khi k <= kích thước
BGT nextLp
MOV ptr, k, LSR # 5; Nhận lời cho cờ [k]
MOV mặt nạ, # 1
VÀ tmp, k, # 31
MOV mặt nạ, mặt nạ, LSL tmp
LDR tmp, [cơ sở, ptr, LSL # 2]
BIC tmp, tmp, mặt nạ, rõ ràng này chút
STR tmp, [cơ sở, ptr, 2 LSL #]; cửa hàng nó trở lại
ADD k, k, gốc; Đỗ tiếp một
B trong khi
.nextLp
ADD i, i, # 1; tiếp theo tôi
CMP i, kích thước
BLE lp
SUBS iter , iter, # 1
BNE mainloop
MOV pc, liên kết; Return sau khi lặp đi lặp lại iTER.
;
.theArray
]
REM trữ các byte cho mảng
P% = P% + SIZE / 8
TIẾP
REM Thời gian 10 lần lặp lại, như trong byte
THỜI gIAN = 0
số nguyên tố = USR sàng
T% = THỜI GIAN
IN "mất"; T% / 100 "giây"; lặp lại "vòng."
Chú ý trình tự mà có được mặt nạ và bù đắp cho một chút được đưa ra trong mảng xảy ra hai lần. Bước đầu tiên là tìm từ bù đắp của từ đó có các yếu tố mong muốn. Có 32 cờ trong một từ, vì vậy các từ có chứa cờ i là i div 32 từ từ bắt đầu của mảng. Việc phân chia 32 được thực hiện bằng cách sử dụng dịch phải bằng năm bit. Tiếp theo, vị trí của lá cờ mong muốn trong vòng từ là cần thiết. Điều này chỉ đơn giản là tôi MOD 32 , mà là thu được sử dụng i và 31 trong lắp ráp. Một mặt nạ, mà bắt đầu giảm ở bit 0, được chuyển bằng cách ( i MOD 32 ) nơi bên trái để có được những mặt nạ đúng. Cuối cùng, để tải các từ có chứa cờ, một tải lập chỉ mục thu nhỏ của các hình thức:
LDR reg, [cơ bản, bù đắp, LSL # 2]
được sử dụng, LSL # 2 rộng từ bù đắp mà chúng tôi tính vào một địa chỉ byte (word-aligned).
Sự khác biệt về tốc độ của phiên bản BASIC và lắp ráp là khá ấn tượng. BASIC mất 6.72 giây để thực hiện một sự lặp lại của chương trình. Assembler mất 0,73 giây để thực hiện mười trong số họ, mà làm cho nó nhanh hơn so với 90 lần. Một phiên bản lắp ráp trong đó phản ánh rõ hơn phiên bản BASIC, với một byte cho mỗi lá cờ, mất 0,44 giây cho mười lần lặp lại.
phân bổ 6,7 Memory
Một số ví dụ của ARM lắp ráp, chúng tôi đã đưa ra dựa trên bộ nhớ đang được có sẵn để lưu trữ dữ liệu. Ví dụ, dây thường được tham chiếu bằng cách sử dụng con trỏ đến các nhân vật trong bộ nhớ, và mảng được điều trị trong cùng một cách. Trong một chương trình mà thao tác rất nhiều dữ liệu, một cách nào đó của bộ nhớ quản lý phải được cung cấp. Ví dụ, một trình soạn thảo văn bản cần để có thể phân bổ không gian để chứa văn bản mà người dùng, và các dịch BASIC cần phải phân bổ không gian cho đường dây chương trình, biến vv
Các cơ sở cung cấp bởi hệ điều hành cho việc phân bổ bộ nhớ khác nhau rất nhiều từ máy tính đến máy tính. Các hệ điều hành UNIX, ví dụ, cung cấp thói quen một số hữu ích 'thư viện' cho phân bổ một số lượng nhất định các byte, giải phóng một khu vực để nó có thể được tái sử dụng sau này, và mở rộng diện tích đã được phân bổ. Mặt khác, một hệ điều hành đơn giản như môi trường được cung cấp bởi một ARM đồng xử lý kết nối với BBC Micro có thể chỉ đưa cho các chương trình một phần lớn bộ nhớ và để quản lý nó hoàn toàn vào chương trình.
Trong phần này, chúng tôi minh họa một cách đơn giản, trong đó cấp phát bộ nhớ có thể được thực hiện, giả định rằng chương trình chỉ được trao một 'hồ bơi' lớn không gian của hệ điều hành.
danh sách liên kết
Trong cấp phát bộ nhớ, một cấu trúc được gọi là danh sách liên kết thường được tìm thấy. Một danh sách liên kết của các đối tượng có thể được coi như là một mảng mà các yếu tố không liền kề nhau trong bộ nhớ. Thay vào đó, mỗi phần tử chứa thông tin rõ ràng về địa chỉ của phần tử tiếp theo. Thông tin này được biết đến như là trường liên kết của các nguyên tố. Mục cuối cùng trong danh sách thường có một giá trị đặc biệt liên lĩnh vực, ví dụ như bằng không, để đánh dấu nó như là cuối cùng, hoặc có thể chứa một liên kết đến các phần tử đầu tiên (điều này được gọi là một danh sách liên kết vòng tròn).
danh sách liên kết có thể được minh họa những bức tranh sử dụng các hộp và mũi tên. Ví dụ, hãy xem xét một danh sách liên kết các hạng mục có chứa hai từ thông tin thêm vào các liên kết. Một danh sách của họ có thể được rút ra như thế này:
Các 'đầu' là một con trỏ tới phần tử đầu tiên. Điều này có thể được lưu trữ trong một vị trí bộ nhớ, hoặc trong một thanh ghi trong ARM. Trường đầu tiên trong mỗi phần tử là con trỏ liên kết, và điều này được theo sau bởi hai từ dữ liệu. Con trỏ cho yếu tố thứ ba không chỉ bất cứ nơi nào; nó đánh dấu sự kết thúc của danh sách.
Có rất nhiều hoạt động có thể được thực hiện trên danh sách liên kết, và phần lớn của nhiều cuốn sách về thuật toán và cấu trúc dữ liệu được dành cho họ. Tuy nhiên, chúng tôi chỉ quan tâm đến một vài thao tác đơn giản ở đây: loại bỏ một mục từ phía trước của danh sách, và thêm một mục mới vào phía trước của danh sách.
Để xóa một mục từ phía trước, chúng ta chỉ cần thay thế con trỏ đầu với các liên kết từ mục đầu tiên. Khi điều này được thực hiện, danh sách có vẻ như thế này:
Chú ý rằng mục số một hiện nay là không thể tiếp cận thông qua danh sách, vì vậy để sử dụng nó, một con trỏ đến nó phải 'nhớ' ở đâu đó. Cũng thông báo rằng nếu các mục bị loại bỏ là người cuối cùng, con trỏ đầu sẽ được đưa ra vào cuối danh sách giá trị, và sẽ không chỉ vào bất cứ điều gì. Điều này được biết đến như một danh sách rỗng.
Để chèn một mục ở phía trước của danh sách, hai hành động được yêu cầu. Đầu tiên, con trỏ đầu được sao chép vào các trường liên kết của các mục sẽ được chèn vào. Sau đó, con trỏ đầu được thay đổi để điểm tại mục mới này.
Với mức này đơn giản của sự hiểu biết về danh sách liên kết, chúng ta có thể mô tả cách chúng được sử dụng trong các chương trình cấp phát bộ nhớ.
phân bổ chuỗi
Đề án phân bổ được trình bày là rất tương tự như BBC BASIC sử dụng để phân bổ không gian cho các biến chuỗi của nó, và như vậy là phù hợp cho các loại ứng dụng đó. Hoạt động mà một chuỗi cấp phát phải thực hiện là:
Phân bổ diện tích. Với chiều dài tính bằng byte, trả về một con trỏ đến một nơi mà con số này của byte có thể được lưu trữ.
Giải phóng một khu vực. Với chiều dài tính bằng byte, và một con trỏ, giải phóng các khu vực bộ nhớ, do đó nó có thể được sử dụng bởi một chuỗi khác khi có yêu cầu.
Strings trong BBC BASIC có thể dài từ 0 đến 255 byte. Việc cấp phát luôn luôn hoạt động trong điều kiện của các từ hoàn chỉnh, vì vậy các chuỗi có thể chiếm từ 0 đến 64 từ. Nhớ lại từ các cuộc thảo luận của các khối thông tin chuỗi trước đó rằng chiều dài được lưu giữ cùng với địa chỉ của các nhân vật tạo nên các chuỗi. Từ byte chiều dài này, số lượng các từ cần thiết để giữ chuỗi có thể suy ra:
từ = (len-1) DIV 4 + 1
Các khu vực của bộ nhớ BASIC sử dụng để lưu trữ chuỗi của nó được gọi là heap. Một con trỏ từ liên kết, chúng tôi sẽ gọi Vartop , giữ địa chỉ của byte miễn phí tiếp theo trên heap. Các giới hạn trên heap được thiết lập bởi người chồng, mà mọc xuống từ phía trên cùng của bộ nhớ.
Một kế hoạch cấp phát bộ nhớ rất đơn giản bằng cách sử dụng đống sẽ làm việc như sau. Để cấp phát n byte, tính toán bao nhiêu từ là cần thiết, sau đó thêm này để Vartop . Nếu đây là lớn hơn so với con trỏ stack, SP, đưa ra một ' Không có chỗ lỗi'. Nếu không, trả về giá trị cũ của Vartop như con trỏ đến không gian tự do, và cập nhật Vartop bởi số lượng thích hợp các byte. Để không gian tự do, không phải làm gì.
Đề án này phải chịu số phận rõ ràng thất bại trong thời gian dài, bởi vì bộ nhớ không bao giờ có thể được giải thoát, và vì vậy cuối cùng Vartop có thể đạt stack và một ' Không có chỗ lỗi' được tạo ra. Để giải quyết điều này, BASIC có một cách để 'trả lại' lưu trữ được sử dụng bởi các chuỗi. Có 64 danh sách liên kết, một cho mỗi số có thể có của những từ mà một chuỗi có thể chiếm. Khi một yêu cầu cho n byte được thực hiện, kiểm tra được thực hiện trên danh sách liên kết thích hợp. Nếu đây không phải là trống rỗng, địa chỉ của mục đầu tiên trong danh sách được trả lại, và điều này được lấy ra từ danh sách. Nếu danh sách rỗng, lưu trữ được lấy từ Vartop như mô tả ở trên. Để n byte miễn phí, khu vực được trả tự do chỉ đơn giản là thêm vào phía trước của danh sách liên kết thích hợp.
Các thuật toán cho phân bổ, miễn phí và khởi được trình bày dưới đây trong BASIC.
DEF PROCinit
EMPTY = 0
DIM danh sách (64)
CHO i = 1 TO 64
danh sách (i) = EMPTY
TIẾP i
​​Vartop = <giá trị ban đầu>
ENDPROC
DEF FNallocate (n)
NẾU n = 0 THEN = 0
từ = (n-1) DIV 4 + 1
NẾU danh sách (từ) <> EMPTY THEN
addr = danh sách (từ)
danh sách (từ) =! danh sách (từ)
ELSE
IF Vartop + 4 * từ> SP THÌ LỖI 0, "Không có chỗ"
addr = Vartop
Vartop + = 4 * từ
ENDIF
= addr
DEF PROCfree (n, addr)
NẾU n = 0 THEN ENDPROC
từ = (n-1) DIV 4 + 1
! addr = danh sách (từ)
danh sách (từ) = addr
ENDPROC
Các phiên bản ARM lắp ráp của những thói quen dựa vào một thanh ghi được gọi là không gian làm việc , mà luôn luôn chứa địa chỉ của sự bắt đầu của một khu vực không gian làm việc cố định. Trong ví dụ này, từ đầu tiên của không gian làm việc giữ giá trị hiện tại của Vartop , và 64 lời tiếp theo là các con trỏ đến danh sách miễn phí. Một đăng ký, heapLimit , được giả định là luôn luôn giữ giới hạn bộ nhớ trên rằng allocater có thể sử dụng. Dưới đây là các phiên bản ARM của ba thói quen.
heapSize = 1000
DIM org 600, đống heapSize-1
addr = 0
n = 1
offset = 2
từ = 3
tmp = 4
heapLimit = 5
Workspace = 6
sp = 13
link = 14
NULL = 0
CHO qua = 0 TO 2 Bước 2
P % = org
[lựa chọn không vượt qua
; Init. Intialise hệ thống phân bổ bộ nhớ
; Nó initialises Vartop và thiết lập các danh sách liên kết 64
; con trỏ đến 'NULL'
.init
STMFD (sp), {link}!
ADR không gian làm việc, ws; Init con trỏ không gian làm việc
BL getVarTop;. Nhận Vartop trong tmp
STR tmp , [không gian làm việc]; Lưu nó
MOV tmp, # NULL; Init các con trỏ
MOV bù đắp, # 64; Lời bù đắp vào không gian làm việc
.initLp
STR tmp, [không gian làm việc, bù đắp, LSL # 2]
SUBS bù đắp, bù đắp, # 1
BNE initLp
LDMFD (sp), {PC};! Return
;
; Alloc. Phân bổ n byte, địa chỉ trở về
; bộ nhớ trong addr, hoặc EMPTY nếu không có chỗ
.alloc
từ SUBS, n, # 1; Return ngay lập tức cho n = 0
MOVMI addr, # NULL
MOVMI PC, liên kết
từ MOV, lời nói, LSR # 2 ; Divide bốn
ADD từ, chữ, # 1; Thêm một
; Nhận con trỏ cho danh sách này
LDR addr, [không gian làm việc, lời nói, LSL # 2]
CMP addr, # NULL;? có trống
LDRNE tmp, [addr]; Không , do đó bỏ liên kết
STRNE tmp, [không gian làm việc, lời nói, LSL # 2]
MOVNE PC, liên kết; Và trở lại
; danh sách rỗng để phân bổ từ Vartop
LDR addr, [Workspace]
ADD tmp, addr, lời nói, LSL # 2; Kiểm tra không có chỗ
CMP tmp, heapLimit
STRLT tmp, [không gian làm việc]; cập nhật Vartop và trở
MOVGE addr, # NULL; Return NULL nếu không có chỗ
MOV PC, liên kết
;
; miễn phí. Hãy kích thước (trong n) và địa chỉ (trong addr)
, trong một chuỗi, và liên kết nó vào thích hợp
;. Danh sách miễn phí
trực tiếp bằng
lời nói SUBS, n, # 1; trở lại, nếu cho n = 0
MOVMI PC, liên kết
từ MOV , lời nói, LSR # 2; Divide bốn
chữ Thanh, lời nói, # 1; Thêm một
; Nhận con trỏ đứng đầu hiện nay với kích thước này
LDR tmp, [không gian làm việc, lời nói, LSL # 2]
STR tmp, [addr]; cửa hàng nó trong khối mới
; cập nhật đầu đến điểm đến mới khối
addr STR, [không gian làm việc, lời nói, LSL # 2]
MOV PC, liên kết; Return
;
; Set tmp để trỏ đến khu vực 'đống'
; và thiết lập giới hạn trên của đống
.getVarTop
ADR tmp, đống
ADD heapLimit, tmp, # heapSize
MOV PC, liên kết
;
.ws EQUD 0; Thời điểm cho Vartop trỏ
]
không gian REM dự trữ cho 64 con trỏ = 256 byte
P% = P% + 256
TIẾP qua
Cách thức mà Vartop được khởi tạo phụ thuộc vào hệ thống. Trong BASIC, ví dụ, Vartop được khởi tạo với giá trị của LOMEM bất cứ khi nào một CLEAR hoạt động kiểu được thực hiện. LOMEM chính thường được đặt trên cùng của chương trình BASIC, nhưng có thể được thay thế bằng cách chuyển nhượng. Ba thói quen cho thấy rằng một số lượng tương đối nhỏ của mã có thể thực hiện phân bổ bộ nhớ khá tinh vi.
Tóm lược
Chúng ta đã thấy rằng hầu hết các loại dữ liệu có thể được nạp vào thanh ghi ARM và xử lý bằng chuỗi ngắn hướng dẫn. các mặt hàng đơn giản có thể được lưu giữ cùng với các chương trình, nhưng chỉ khi chương trình đang thực thi trong RAM. chương trình ROM chỉ có thể truy cập vào bảng cố định của dữ liệu trong chương trình học. Các dữ liệu khác phải được truy cập thông qua thanh ghi con trỏ, sử dụng bộ nhớ được cấp bởi hệ điều hành. Dữ liệu nên được truy cập một cách độc lập vị trí.

0 Comment:

Đăng nhận xét

Thank you for your comments!