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

4. BBC BASIC Assembler
Có hai cách chính để viết chương trình hợp ngữ ARM. Một là sử dụng một nhà lắp ráp chuyên dụng. một chương trình như vậy phải mất một tập tin văn bản có chứa các lệnh ngôn ngữ lắp ráp ARM, lắp ráp nó, và tạo ra một tập tin có chứa mã máy tương đương. Hai tập tin được gọi là các file nguồn và file đối tượng tương ứng.
Một phương pháp khác là sử dụng lắp ráp xây dựng trong để BBC BASIC. Khả năng pha trộn lắp ráp với BASIC là một tính năng rất hữu ích của ngôn ngữ, và một trong đó là tương đối đơn giản để sử dụng.Vì lý do này, và vì sự phổ biến của BBC BASIC, chúng tôi mô tả làm thế nào để sử dụng được xây dựng trong lắp ráp của nó. Các ví dụ của hai chương tiếp theo cũng có định dạng mong đợi của các nhà lắp ráp BASIC.
4.1 Các nguyên tắc đầu tiên
Hai 'tuyên bố "đặc biệt được sử dụng để nhập và thoát khỏi lắp ráp. Các nhân vật khung vuông mở, [ , đánh dấu sự bắt đầu của mã nguồn ngôn ngữ lắp ráp. Bất cứ khi nào nhân vật này đang gặp phải nơi BASIC hy vọng sẽ nhìn thấy một tuyên bố như PRINT hoặc chuyển nhượng, BASIC dừng thực hiện chương trình và bắt đầu lắp ráp các hướng dẫn ARM sang mã máy. Sự kết thúc của nguồn được đánh dấu bằng dấu ngoặc vuông gần, ] . Nếu đây là đọc ở đâu BASIC được mong đợi để xem một chỉ dẫn để lắp ráp, nó rời khỏi chế độ lắp ráp và bắt đầu thực hiện các (BASIC) lại chương trình.
Để xem hiệu quả của ra vào lắp ráp, nhập vào chương trình ngắn này:
10 PRINT "Outside the assembler"
20 [ ;In the assembler
30 ]
40 PRINT "Outside the assembler"
Nếu bạn RUN này, bạn sẽ thấy một cái gì đó như sau:
Outside the assembler
00000000 ;In the assembler
Outside the assembler
Giữa hai dòng sản phẩm của IN báo cáo là một trong đó lắp ráp in. Trừ khi bạn nói với nó không, lắp ráp in một danh sách lắp ráp. Chúng tôi sẽ mô tả chi tiết này, nhưng bây giờ đủ để nói rằng nó bao gồm ba phần: một địa chỉ (tám ​​số không ở trên, mà có thể khác nhau khi bạn chạy chương trình), thì lệnh mã máy trong hex, và nguồn gốc hướng dẫn đang được lắp ráp.
Trong ví dụ trên, không có hướng dẫn được lắp ráp, vì vậy không có mã máy được liệt kê. Dòng duy nhất của nguồn là một nhận xét. Các dấu chấm phẩy, ; , giới thiệu một dòng bình luận để lắp ráp. Điều này hoạt động nhiều như là một REM trong BASIC, và gây ra các văn bản sau khi nó được bỏ qua. Các bình luận được thụt vào phần nào trong danh sách lắp ráp, như lắp ráp rời khỏi không gian cho bất kỳ mã máy mà có thể được sản xuất.
Các địa chỉ được in ở đầu dòng được gọi là địa điểm truy cập. Điều này cho chúng ta hai điều: nơi lắp ráp được đặt hướng dẫn mã máy lắp ráp trong bộ nhớ, và những gì giá trị của bộ đếm chương trình (PC) sẽ được khi hướng dẫn nào lấy về để thực hiện, khi chương trình cuối cùng đã đến được chạy. Bởi vì chúng tôi không được lắp ráp bất kỳ hướng dẫn, không có mã số sẽ được ghi vào bộ nhớ, vì vậy nó không có vấn đề gì giá trị vị trí truy cập là. Thông thường, tuy nhiên, khi bạn lắp ráp mã, bạn nên thiết lập vị trí truy cập đến một số địa chỉ nơi lắp ráp có thể lưu trữ các mã máy.
Nó rất dễ dàng để thiết lập vị trí truy cập, vì nó được lưu trữ trong hệ thống số nguyên biến P%. Nếu bạn nhập vào các thông điệp trực tiếp:
IN ~ P%
bạn sẽ thấy những con số tương tự như in vào lúc bắt đầu của đường trung tuyến trên (trừ số không hàng đầu).
Bây giờ chúng ta lắp ráp một số mã thực tế. Để làm điều này, chúng tôi cần phải dự trữ một số lưu trữ cho mã đối tượng. Điều này là dễ dàng thực hiện bằng cách sử dụng hình thức DIM mà giữ một số lượng nhất định các byte. Gõ như sau:
10 DIM org 40
20 P% = org
30 [ ;A simple ARM program
40 MOV R0,#32
50.LOOP
60 SWI 0
70 ADD R0,R0,#1
80 CMP R0,#126
90 BNE LOOP
100 MOV R15,R14
110 ]
Nó có vẻ lạ khi sử dụng biến org trong DIM , sau đó sử dụng nó chỉ một lần để đặt P%, nhưng chúng ta sẽ thấy lý do cho điều này sau. Lưu ý rằng các DIM tuyên bố trả về một địa chỉ mà được đảm bảo để được xếp trên một ranh giới từ, do đó không cần phải 'lực' nó là một bội số của bốn.
Như trong ví dụ trước, dòng đầu tiên của các nhà lắp ráp là một nhận xét. Bảy dòng tiếp theo mặc dù là hướng dẫn ngôn ngữ lắp ráp thực tế. Trong thực tế, bạn có thể nhận ra các chương trình như là rất tương tự như ví dụ đưa ra ở cuối Chương Một. Đó là một vòng lặp đơn giản, trong đó in ký tự ASCII từ 32 (không gian) để ASCII 126 (dấu ngã hay ~ ).
Các dòng được thụt vào bởi một không gian là ARM thuật nhớ, tiếp theo là các toán hạng của họ. Khi lắp ráp gặp này, nó sẽ chuyển đổi các hướng dẫn vào mã máy bốn byte thích hợp, và sau đó lưu trữ này tại địa điểm truy cập hiện tại - P%. Sau đó, P% sẽ tăng lên bốn cho lệnh tiếp theo.
Dòng 50, bắt đầu với một . , Là một nhãn. Nhãn là một biến đánh dấu vị trí hiện tại trong chương trình. Khi gặp một nhãn, lắp ráp lưu trữ các giá trị hiện tại của địa điểm truy cập vào các biến, vì vậy mà nơi này trong chương trình có thể được truy cập trong một hướng dẫn sau. Trong trường hợp này BNE tại dòng 90 sử dụng nhãn LOOP chi nhánh trở lại SWI hướng dẫn. Bất kỳ biến số có thể được sử dụng như là một nhãn, nhưng theo quy ước biến điểm nổi (không có % dấu) được sử dụng. Mỗi nhãn trong một chương trình nên có một tên duy nhất. Đây không phải là khó khăn để đạt được như BBC BASIC cho phép tên rất dài.
Nếu bạn RUN chương trình, một danh sách lắp ráp sẽ được sản xuất, thời gian này tìm kiếm như:
000167C8; Một chương trình ARM đơn giản
000167C8 E3A00020 MOV R0, # 32
000167CC .LOOP
000167CC EF000000 SWI 0
000167D0 E2800001 ADD R0, R0, # 1
000167D4 E350007E CMP R0, # 126
000167D8 1AFFFFFB BNE LOOP
000167DC E1A0F00E MOV R15, R14
Cột đầu tiên là vị trí truy cập, và trong danh sách trên, chúng ta có thể thấy rằng các lệnh máy đầu tiên được đặt tại địa chỉ & 167C8. Tiếp theo là các đại diện hex của hướng dẫn đó, tức là các mã mà thực sự sẽ được lấy và thực hiện bởi bộ xử lý. Bạn có thể xác minh rằng đây là những gì đã được lưu trữ bởi các nhà lắp ráp bằng cách gõ:
IN ~! & 167C8
(Các địa chỉ nên được bất cứ điều gì đã được in khi bạn chạy chương trình.) Bạn sẽ thấy E3A00020 , như trong danh sách.
Trong cột thứ ba là nhãn các chỉ dẫn hướng, hoặc các không gian nếu không có một, tiếp theo là phần còn lại của các dòng, như đánh máy của bạn.
Để xem thành quả của bạn lao động, gõ lệnh:
org CALL
Các ký tự ASCII thiết lập của máy sẽ được in. Các CALL tuyên bố mất địa chỉ của một thói quen mã máy, mà nó sau đó thực hiện. Các mã máy được gọi là nếu một BL hướng dẫn đã được sử dụng với địa chỉ nêu như toán hạng. Vì vậy, để trở về BASIC, địa chỉ trở lại trong R14 được chuyển giao cho R15 (PC) như trong ví dụ trên.
Chúng tôi sẽ có nhiều điều để nói về việc sử dụng CALL (và các chức năng liên quan USR ) sau trong chương này.
4.2 Passes và các tùy chọn lắp ráp
Thông thường, một nhãn được xác định sau khi các vị trí (hoặc nơi) trong chương trình mà từ đó nó được sử dụng. Ví dụ, một chi nhánh về phía trước có thể có hình thức này:
100 CMP R0, # 10
110 BNE notTen
120; một số hướng dẫn
130; ...
140.notTen
Trong ví dụ này, nhãn notTen đã không được gặp phải khi giảng dạy tại dòng 110 được lắp ráp. Nó không được xác định cho đến khi dòng 140. Nếu đây hoặc một chuỗi tương tự như các hướng dẫn đã được bắt gặp trong khi lắp ráp một chương trình sử dụng các phương pháp chúng tôi đã mô tả, một thông báo của các loại ' Unknown hoặc mất tích biến ' sẽ được sản xuất.
Chúng tôi rõ ràng cần phải có khả năng lắp ráp chương trình với tài liệu tham khảo về phía trước, và lắp ráp BBC BASIC thông qua các phương pháp tương tự để các vấn đề như nhiều người khác: nó làm cho hai quét, hoặc đi qua, trong chương trình. Người đầu tiên là để cho phép tất cả các nhãn được xác định, và lần thứ hai, lắp ráp các mã thích hợp.
Trong assembler BASIC chúng tôi nói với nó để ngăn chặn "lỗi" trong suốt đầu tiên vượt qua bằng cách sử dụng các OPT chỉ thị. Một chỉ thị (hoặc giả-op như họ cũng được gọi) là một hướng dẫn mà không được chuyển đổi thành mã máy, nhưng thay vì chỉ thị các lắp ráp để thực hiện một số hành động. Nó được sử dụng trong cùng một vị trí như là một hướng dẫn thích hợp.
Các OPT chỉ thị được theo sau bởi một số, và thường được đặt ngay sau khi [ , nhưng trong thực tế có thể được sử dụng bất cứ nơi nào trong mã nguồn. Con số này được giải thích như là một giá trị ba-bit. Bit không kiểm soát việc niêm yết, lắp ráp; chút một điều khiển cho dù ' Biến không xác định hoặc mất tích "lỗi loại đang bị đàn áp hay không, và cắn hai điều khiển một cái gì đó gọi là bù đắp, lắp ráp, mà chúng ta sẽ đến sau. Tám giá trị có thể có thể được tóm tắt như sau:
Giá trị
lắp ráp offset
lỗi cho
Liệt kê
0KhôngKhôngKhông
1KhôngKhôngVâng
2KhôngVângKhông
3KhôngVângVâng
4VângKhôngKhông
5VângKhôngVâng
6VângVângKhông
7VângVângVâng
Nếu bạn không sử dụng OPT ở tất cả, tình trạng mặc định là 3 - lắp ráp bù đắp không được sử dụng; lỗi không bị đàn áp, và một danh sách được đưa ra.
Cách dễ nhất để có được hai đường chuyền trong BASIC là để kèm theo toàn bộ mã nguồn trong một CHO ... Tiếp theo vòng lặp, thực hiện hai lần lặp lại. Thông thường, các mã được đính kèm trong một thủ tục để các CHO và TIẾP vòng lặp có thể được gần gũi với nhau. Các tùy chọn lắp ráp mong muốn sau đó có thể được thông qua như là một tham số trong thủ tục.
Dưới đây là một ví dụ sau đây phác thảo mô tả ở trên. Chương trình bao gồm một tham chiếu về phía trước để minh họa việc sử dụng hai qua. Việc niêm yết được sản xuất tại đèo thứ hai (vì nó luôn luôn cần được nếu có hai đường chuyền), vì vậy các giá trị cho đến OPT là 0 và 3 tương ứng.
1000 REM Example of two-pass assembly
1010 DIM org 100
1030 FOR pass=0 TO 3 STEP 3
1040 PROCasm(pass,org)
1050 NEXT pass
1060 END
1070
2000 DEF PROCasm(pass,org)
2010 P%=org
2020 [ OPT pass
2030 .loop
2040 SWI 4 ;Read a char
2050 MOVS R0,R0,ASR#1
2055 MOVEQ R15, R14
2060 BCC even
2070 SWI 256+ASC"O" ;Print O
2080 B loop
2090 .even
2100 SWI 256+ASC"E" ;Print E
2110 B loop
2120 ]
2130 ENDPROC
Điều quan trọng nhất cần ghi nhận là các biến% P được đặt ở đầu mỗi đường chuyền. Nếu bạn chỉ đặt nó tại đầu đèo đầu tiên, vào cuối đèo đầu tiên nó sẽ chứa địa chỉ của cuối của mã này, và không chính xác cho sự khởi đầu của đèo thứ hai.
Chương trình này là khá tầm thường. Nó đọc ký tự từ bàn phím và in E nếu nhân vật có một thậm chí mã ASCII, hoặc O nếu không. Điều này lặp đi lặp lại cho đến khi một ký tự ASCII 0 hoặc 1 ( CTRL @ hoặcCTRL A ) được gõ. Các chi nhánh để thậm chí tại dòng 2060 là một tiền đạo một; trong thời gian qua trước thực tế là thậm chí chưa được xác định nhưng được bỏ qua, sau đó trong thời gian qua thứ hai, giá trị của nó - đặt trong dòng 2090 - được sử dụng để giải quyết sau SWI .
Trong thực tế, bởi vì các hướng dẫn điều kiện của ARM, chương trình này có thể đã được viết mà không có bất kỳ chi nhánh về phía trước, bằng cách làm cho dòng 2070 và 2080:
2070 SWICS 256+ "O"; In O
2080 SWICC 256+ "E"; In E
và xóa dòng 2090 và 2100. Nó luôn luôn khó nghĩ cách đơn giản nhưng hữu ích để minh họa điểm cụ thể.
Bạn nên biết về những gì thực sự xảy ra khi một tài liệu tham khảo về phía trước là gặp phải trong qua đầu tiên. Thay vì đưa ra một lỗi, lắp ráp sử dụng giá trị hiện tại của địa điểm truy cập. Trong dòng 2060 trên, ví dụ, giá trị trả về cho biến không xác định là địa chỉ của BCC hướng dẫn của chính nó. Nếu chỉ có đèo đầu tiên đã được thực hiện và bạn đã cố gắng để gọi mã, một vòng lặp vô hạn sẽ được gây ra bởi chi nhánh này cho chính nó. Điều quan trọng là, do đó, rằng bạn luôn luôn thiết lập bit 1 của OPT giá trị trong thời gian qua thứ hai, để cho phép các lỗi được báo cáo, thậm chí nếu bạn không bận tâm về việc niêm yết.
phong cách Chương trình 4.3 và khả năng đọc
Bất kỳ kinh nghiệm lập trình ngôn ngữ assembler đọc chương này có thể sẽ được suy nghĩ của bây giờ, 'Thật là một tải của rác.' Các ví dụ được trình bày cho đến nay là không, nó phải được thừa nhận, các mô hình rõ ràng và sang trọng. Đây là cố ý; nó mang lại cho chúng ta một cơ hội để chỉ ra làm thế nào họ đang thiếu và làm thế nào những thiếu hụt này có thể được khắc phục.
Như chúng ta đã học ở Chương Một, ngôn ngữ lắp ráp là về như xa từ lý tưởng như là một lập trình viên người bình thường có khả năng liên doanh. Khi viết một chương trình hợp ngữ, bạn nên do đó cung cấp cho người đọc như giúp nhiều càng tốt trong việc tìm hiểu những gì đang xảy ra. Ghi nhớ rằng người đọc cũng có thể là bạn, vì vậy nó không chỉ là một câu hỏi giúp đỡ người khác.
Bình luận là dòng đầu tiên của cuộc tấn công. Như chúng ta đã thấy, ý kiến có thể xuất hiện như là điều đầu tiên (và duy nhất) trên đường dây, hoặc sau khi nhãn, hoặc sau khi nhãn và hướng dẫn. Một bình luận có thể không đến giữa nhãn và hướng dẫn, như hướng dẫn sẽ được bỏ qua, được thực hiện như là một phần của bình luận. Chúng tôi đã sử dụng ; ​​để giới thiệu ý kiến, vì đây là cùng một nhân vật mà nhiều nhà lắp ráp khác sử dụng. BBC BASIC lắp ráp cũng cho phép bạn sử dụng \ và REM . Dù bạn sử dụng, bạn phải nhất quán.
Một nhận xét nên có ý nghĩa. Các ý kiến ​​tại sự bắt đầu của một thói quen cần giải thích những gì nó làm, những gì các điều kiện nhập cảnh, xuất cảnh, và những gì các tác dụng phụ là. Xuất nhập cảnh điều kiện thường liên quan đến các nội dung của các thanh ghi ARM khi thường xuyên được gọi và khi nó trở về, nhưng cũng có thể bao gồm các trạng thái của bộ nhớ vị trí cụ thể tại những điểm này quá.
Các tác dụng phụ của một thói quen tương tự để nói những gì nó làm, nhưng quan tâm nhiều hơn với những hành động mà có thể ảnh hưởng đến hoạt động của các thói quen khác. Một tác dụng phụ thường gặp là các thanh ghi nào đó hư hỏng. Một ví dụ khác có thể là một thói quen mà in một ký tự trên màn hình như hành động chính của nó, nhưng corrupts một con trỏ trong bộ nhớ RAM là một tác dụng phụ.Bằng cách ghi lại những điều này trong các ý kiến, bạn làm cho nó dễ dàng hơn để theo dõi lỗi, và để giao tiếp thói quen của bạn với nhau.
Từ những nhận xét trên, có thể thấy rằng các thói quen trình bày ở trên là kém ăn cùng với ý kiến, và điều này làm cho nó khó khăn hơn để hiểu họ, mặc dù họ là khá ngắn.
Một cách quan trọng của việc tăng khả năng đọc là sử dụng tên thay vì các con số. Ví dụ nơi này che mờ các vấn đề trong chương trình trước đó là:
80 CMP R0, # 126
2040 SWI 4; Đọc một char
Phải thừa nhận rằng các ví dụ thứ hai có một bình luận đó cung cấp cho người đọc một số ý tưởng, nhưng một trong những đầu tiên là hoàn toàn vô nghĩa đối với nhiều người. Trong assembler BBC BASIC, bất cứ nơi nào có một số là cần thiết trong một toán hạng, toàn bộ sức mạnh của người đánh giá biểu hiện BASIC có sẵn. Cách rõ ràng nhất để sử dụng này là sử dụng các biến như là tên cho con số 'ma thuật'.Ví dụ, trong các chương trình nhân vật, chúng ta có thể bao gồm một bài tập
17 maxChar = ASC "~"
và làm cho dòng 80:
80 CMP R0, #maxChar
Ở nơi đầu tiên chúng ta được kể trong dòng 17 những gì các nhân vật cuối cùng được in là, và thứ hai tên maxChar đưa ra một chỉ dẫn tốt hơn nhiều trong dòng 80 của những gì chúng ta đang so sánh.
Trong ví dụ thứ hai, các toán hạng để SWI là một số mã để cho hệ điều hành gì thường được mong muốn. Đây là thói quen ký tự đọc mà đi bằng tên của ReadC . Vì vậy, chương trình có thể được thực hiện dễ đọc hơn bằng cách thêm dòng:
1005 ReadC = 4: REM SWI để đọc một char vào R0
và thay đổi dòng 2040 để:
2040 SWI ReadC
mà làm cho những nhận xét ​​cần thiết. Các SWI 256 là một hướng dẫn có thể được cải thiện. Điều này đặc biệt SWI có một toán hạng của các hình thức 256 + ch, nơi ch là mã của ký tự cần in. Nó được đưa ra tên WriteI (cho viết ngay lập tức) trong tài liệu Acorn, vì vậy chúng tôi có thể có một bài tập như thế này:
1007 WriteI = & 100: REM SWI viết char trong LSB của toán hạng
và thay đổi các hướng dẫn để có các hình thức:
2070 SWI WriteI + ASC "O"
Tầm quan trọng của những cái tên đến những thứ như SWI mã số và toán hạng ngay lập tức không thể phủ nhận. Một công cụ hữu ích tương tự được đặt tên đăng ký. Vì vậy, đến nay chúng tôi đã sử dụng các denotations R0, R1 vv để đứng cho đăng ký. Ngoài những cái tên tiêu chuẩn, lắp ráp chấp nhận một biểu thức mà đánh giá một số đăng ký. Một ứng dụng rõ ràng của việc này là để cung cấp cho tên để đăng ký quan trọng như các SP bằng cách gán các số đăng ký thích hợp cho một biến. Ví dụ, nếu chúng ta có ba bài tập ở phần BASIC của chương trình:
10 link = 14 : REM BL link register
20 sp = 13 : REM My stack pointer register
30 acc = 5 : REM Accumulator for results
sau đó hướng dẫn có thể mang hình thức:
1000 MOVCS pc,link ;Return if too big
1210 STM (sp)!,{acc,link} ;Save result and return addr
2300 LDM (sp)!,{acc,pc} ;Return and restore
Có hai điều cần lưu ý về các ví dụ. Việc đầu tiên là chúng ta sử dụng tên máy tính mặc dù đây wan't một trong các biến chúng ta định nghĩa ở trên. Việc lắp ráp BASIC nhận ra nó tự động và thay thế R15 .Thứ hai là sp có dấu ngoặc xung quanh nó. Đây là những yêu cầu bởi vì ! Dấu hiệu được sử dụng bởi BASIC để biểu thị 'gián tiếp'. Để ngăn chặn BASIC khỏi bị nhầm lẫn bạn phải đặt dấu ngoặc xung quanh bất kỳ tên biến được sử dụng trước khi một ! Trong STM và LDM hướng dẫn. Bạn không cần dấu ngoặc nếu một tên đăng ký, chẳng hạn như R13 , được sử dụng.
Trong các ví dụ trong cuốn sách này, chúng tôi sử dụng tên máy tính , liên kết và sp như trên, và chúng tôi cũng sử dụng tên cho máy khác nếu có thể. Không có sự nhầm lẫn giữa các số đăng ký và toán hạng ngay lập tức, tất nhiên, bởi vì sau này đều bắt đầu bằng một # dấu. Vì thế:
4310 MOV acc, sp
bản sao nội dung của đăng ký sp (tức là R13) để đăng ký acc , trong khi đó:
4310 MOV acc, # sp
sẽ tải các giá trị tức 13 vào acc đăng ký.
Điểm cuối cùng về phong cách liên quan bố trí. Việc lắp ráp không áp đặt quá nhiều trở ngại trên con đường mà bạn đặt ra các chương trình. Như chúng tôi đã đề cập, nhãn phải đến ngay lập tức sau khi số dòng, và ý kiến phải xuất hiện như là (và có thể chỉ) điều cuối cùng trên đường dây. Tuy nhiên, nó được khuyến khích để dòng lên các cột nơi có thể. Điều này làm cho nó dễ dàng hơn để sau ý kiến nếu không có gì khác. Comments mô tả thói quen cá nhân hoặc toàn bộ chương trình có thể được đặt ở đầu dòng. Điều này cũng áp dụng cho ý kiến để được hướng dẫn trong đó sẽ không phù hợp trên đường một cách dễ dàng (ví dụ như sau một STR với một danh sách đăng ký dài).
Đường dây khoảng cách cũng có thể là quan trọng. Đó là một ý tưởng tốt để lại dòng trống giữa thói quen để phân biệt chúng.
Một định dạng điển hình sẽ là: mười không gian cho các nhãn, 25 để được hướng dẫn, và phần còn lại của đường cho các nhận xét. Ví dụ:
1230 MOV count, #15 ;Init the counter
1240.loop SWI writeI+ASC"*" ;Print an asterisk
1250 SUB count, #1 ;Next count
1260 BNE loop
Một điểm nhỏ của phong cách trong danh sách trên là khoảng cách giữa các toán hạng. Nó là dễ dàng hơn để tách các toán hạng nếu có ít nhất một nhân vật không gian giữa chúng.
Bằng cách của một tương phản đơn giản, chúng tôi liệt kê các chương trình đầu tiên của chương này, cố gắng thực hành điều đó mà chúng ta vừa được rao giảng.
10 DIM org 40
20 P% = org
30 outReg = 0 : REM Register use by WriteC. Preserved
40 link = 14 : REM BL link register
50 pc = 15 : REM ARM program counter
60 WriteC = 0 : REM SWI code to print char in R0
70 firstChar = ASC" "
80 lastChar = ASC"~"
90 [
100 ;A simple ARM program. This prints the ASCII
110 ;character set from 'firstChar' to 'lastChar'
120 MOV outReg, #firstChar ;Init the output char
130.loop
140 SWI WriteC ;Display the char.
150 ADD outReg, outReg, #1 ;Increment the character
160 CMP outReg, #lastChar ;Finished?
170 BNE loop ;No so loop
180 MOV pc, link ;else return
190 ]
Bởi vì số dòng không đặc biệt có liên quan đến ngôn ngữ lắp ráp, các danh sách trong các chương sau không sử dụng chúng.
Cuối cùng, một lưu ý về trường hợp. Việc lắp ráp là hoàn toàn hợp không nhạy cảm. Dễ nhớ và 'R trong R0 vv có thể được trong trường hợp trên hoặc thấp hơn hoặc bất kỳ sự kết hợp đó. Nhãn, tuy nhiên, phải tuân theo các quy tắc của phần còn lại của BASIC, trong đó ghi rằng tên biến là trường hợp phụ thuộc. Như vậy LOOP là một nhãn hiệu khác nhau từ vòng lặp , và lặp lại là khác nhau. Quy ước sử dụng trong cuốn sách này là trường hợp trên cho dễ nhớ và R0 kiểu định danh, và hỗn hợp các trường hợp trên và thấp hơn (ví dụ như strPtr ) cho các nhãn và các tên khác. Như thường lệ, các phương pháp bạn áp dụng là tùy thuộc vào bạn, nhất quán là điều quan trọng.
4.4 lắp ráp offset
Các cơ sở của hội bù đắp được đề cập ở trên. Nó được kích hoạt bởi bit 2 của OPT biểu. Khi lắp ráp bù đắp được sử dụng, các mã được vẫn còn lắp để thực hiện tại các địa chỉ trong biến P%. Tuy nhiên, nó không được lưu trữ ở đó. Thay vào đó, nó được đặt tại địa chỉ tổ chức tại các biến% O.
Động lực để lắp ráp bù đắp có thể được giải thích như sau. Giả sử bạn đang viết một chương trình mà sẽ được tải và thực hiện tại địa chỉ & 8000. Bây giờ, sử dụng DIM để có được không gian mã, bạn tìm thấy bạn chỉ có thể có được địa chỉ từ khoảng 8700 & lên. Bạn có thể thiết lập P% một cách rõ ràng để & 8000, nhưng vì đây là nơi không gian làm việc thông dịch BASIC của nói dối, bạn sẽ gặp nguy hiểm nghiêm trọng của ghi đè lên nó. Điều này luôn luôn dẫn đến sự không vui.
Các giải pháp cho tình trạng khó xử này là vẫn gán P% đến & 8000, như thể nó sẽ thực hiện ở đó, nhưng sử dụng bù đắp lắp ráp để lưu trữ các mã máy tại một số thuận tiện DIM vị trí med. Dưới đây là những phác thảo của một chương trình sử dụng lắp ráp bù đắp, không có số dòng.
org = &1000 : REM Where we want the code to run
DIM code 500 : REM Where the code is assembled to
code$= " "+STR$~code+" "
org$ = " "+STR$~org+" "
FOR pass=4 TO 6 STEP 2
P%=org
O%=code
[ OPT pass
;The lines of the program come here
;...
;...
]
NEXT pass
OSCLI "SAVE PROG "+code$+STR$~O%+org$+org$
Cả hai P% và O% được đặt ở đầu mỗi đường chuyền. Bởi vì bit 2 của vượt qua giá trị lặp được thiết lập, lắp ráp bù đắp sẽ diễn ra và các mã lắp ráp sẽ thực sự được lưu trữ từ các địa chỉ trong  . Khi lắp ráp, các mã được lưu với các địa chỉ tải và thực hiện phù hợp, và có thể được kích hoạt bằng cách sử dụng * lệnh.
Khi sử dụng lắp ráp bù đắp, bạn không nên sử dụng CALL để kiểm tra mã máy, vì nó sẽ được lắp ráp để thực hiện tại một địa chỉ khác nhau từ một trong đó nó được lưu trữ.
Sau khi đã minh họa việc sử dụng lắp ráp bù đắp, bây giờ chúng ta sẽ nói rằng bạn nên ít khi phải sử dụng nó. Như các chương tiếp theo cố gắng để hiển thị, nó là khá dễ dàng để viết các chương trình ARM mà hoạt động một cách chính xác tại bất kỳ địa chỉ tải. Các chương trình này được gọi là vị trí độc lập, và có một vài quy tắc đơn giản, bạn phải tuân theo để cung cấp cho các chương trình của bạn sở hữu mong muốn này.
Ở trang sau là một phiên bản của đề cương liệt kê ở trên, giả sử rằng chương trình giữa [ và ] tuyên bố là vị trí độc lập.
org = &1000 : REM Where we want the code to run
DIM code 500 : REM Where the code is assembled to
code$= " "+STR$~code+" "
org$ = " "+STR$~org+" "
FOR pass=0 TO 2 STEP 2
P%=code
[ OPT pass
;The lines of the program come here
;...
;...
]
NEXT pass
OSCLI "SAVE PROG "+code$+STR$~P%+org$+org$
Sự khác biệt đó O% không nhận được một đề cập đến, và các giá trị cho OPT chỉ là 0 và 2 tương ứng. Giá trị của org chỉ được sử dụng để thiết lập các địa chỉ tải và thực hiện trong LƯU lệnh.
Trong ánh sáng của các bên trên, bạn có thể tự hỏi tại sao bù đắp lắp ráp được cho phép cả. Nó thực sự là một kết chuyển từ phiên bản đầu của BBC BASIC, chạy trên BBC Micro. Các nhà lắp ráp kết hợp cho 6.502 bộ xử lý cũ mà máy BBC sử dụng. Nó hầu như không thể viết vị trí mã độc lập cho 6502, để bù đắp lắp ráp là rất hữu ích, đặc biệt là đối với những văn bản 'paged ROM phần mềm.
Hiện vẫn còn là một sử dụng hợp lệ để lắp ráp bù đắp, và đó là trong việc xác định các khu vực của bộ nhớ để lưu trữ dữ liệu. Chúng ta sẽ mô tả điều này trong phần tiếp theo.
4.5 Định nghĩa không gian
Hầu hết các chương trình cần một số khu vực lưu trữ dữ liệu có liên quan. Điều này có thể được sử dụng cho các dữ liệu liên tục, chẳng hạn như bảng các lệnh, hoặc để giữ các đồ vật như chuỗi ký tự (tên tập tin, vv) hoặc các mảng của con số đó sẽ không phù hợp vào sổ đăng ký của ARM. Có bốn chỉ lắp ráp cho phép bạn đặt chỗ trong chương trình cho dữ liệu. Chúng được gọi là:
EQUB - Dự trữ một byte EQUW - Dự trữ một từ (hai byte) EQUD - Dự trữ một từ kép (bốn byte) EQUS - Dự trữ một chuỗi (0 đến 255 byte)


Các tham chiếu đến một từ '' là hai byte có thể gây nhầm lẫn trong ánh sáng của tuyên bố trước đó của chúng tôi rằng ARM có một 32-bit hoặc bốn byte chiều dài từ. Giống như lắp ráp bù đắp, các ký hiệu có nguồn gốc của nó trong lịch sử âm u của BBC BASIC. Đây có thể là tốt nhất để quên đi những gì các chữ đứng, và chỉ cần nhớ bao nhiêu byte mỗi dự trữ chỉ thị.
Mỗi phòng trong số các chỉ thị được theo sau bởi một biểu thức, một số nguyên trong ba trường hợp đầu tiên, và một chuỗi ở cuối cùng. Việc lắp ráp nhúng số lượng thích hợp các byte vào mã máy và số gia P% (và nếu cần thiết O%) của số tiền phải.
Dưới đây là một ví dụ của việc sử dụng EQUB. Giả sử chúng ta đang viết một trình điều khiển VDU nơi ký tự điều khiển (trong phạm vi 0..31) có thể được theo sau bởi 1 hoặc nhiều tham số 'byte. Chúng tôi sẽ yêu cầu một bảng trong đó, đối với mỗi mã, đã cho chúng tôi số lượng tham số. Điều này có thể được thiết lập bằng cách sử EQUB như dưới đây:
.parmCount
; This table holds the number of parameters bytes
; for the control codes in the range 0..31
EQUB 0 ;NUL. Zero parameters
EQUB 1 ;To Printer. One parm.
EQUB 0 ;Printer on. Zero parms.
EQUB 0 ;Printer off. Zero parms
.... ....
.... ....
EQUB 4 ;Graf origin. Four bytes
EQUB 0 ;Home. No parameters
EQUB 2 ;Tab(x,y). Two parms.
; End of parmCount table
;
Mỗi phòng trong số EQUB s nhúng byte đếm được như toán hạng vào mã máy. Sau đó, P% được chuyển vào một. Trong trường hợp của EQUW , hai LSBs của các đối số được lưu trữ (byte thấp nhất đầu tiên) và P% tăng thêm hai. Đối với EQUD , tất cả bốn byte của toán hạng số nguyên được lưu trữ để ARM tiêu chuẩn (byte thấp nhất đến cao nhất byte) và P% được tăng lên 4.
Có một vài điểm cần lưu ý. Chúng tôi đang lưu trữ dữ liệu trong mã đối tượng, không hướng dẫn. Vì vậy, bạn phải đảm bảo rằng chương trình sẽ không cố gắng để 'thực hiện' các byte nhúng bằng EQUB vv Ví dụ, nếu có đang ngay trước nhãn parmCount trên, không nên có đường cho mã để 'thả qua' vào khu vực dữ liệu. Kết quả của việc thực hiện dữ liệu là không thể đoán trước, nhưng lúc nào khó chịu. Vì lý do này, nó là khôn ngoan để tách mã '' và 'dữ liệu' của chương trình càng nhiều càng tốt.
Thứ hai, trong ví dụ trên, chúng ta nhúng 32 byte, mà là một bội số của bốn. Vì vậy, giả sử rằng P% bắt đầu trên một ranh giới từ, nó sẽ kết thúc trên một sau khi danh sách các EQUB s. Điều này có nghĩa rằng nếu chúng ta bắt đầu lắp ráp các hướng dẫn sau khi bảng, sẽ không có vấn đề. Giả sử, tuy nhiên, chúng tôi thực sự nhúng 33 byte. Sau này, P% sẽ không còn nằm trên ranh giới từ, và nếu chúng ta muốn bắt đầu để lắp ráp các hướng dẫn sau khi bảng, một số hành động khắc phục sẽ phải được thực hiện. Các ALIGN chỉ được sử dụng để thực hiện điều này.
Bạn có thể sử dụng ALIGN bất cứ nơi nào mà một lệnh có thể được sử dụng. Tác dụng của nó là để buộc P% và O% để chuyển sang ranh giới từ tiếp theo. Nếu họ đã là word-aligned, họ không bị thay đổi. Nếu không, họ được tăng thêm 1, 2 hoặc 3 là thích hợp, để các giá trị mới là bội số của bốn.
Chỉ thị của chuỗi, EQUS , cho phép chúng tôi để nhúng một chuỗi các byte đại diện bởi một chuỗi BBC BASIC vào bộ nhớ. Ví dụ, giả sử chúng ta có một bảng tên lệnh để nhúng vào một chương trình. Mỗi tên được chấm dứt bằng việc có các bit đầu của bộ lá thư cuối cùng của nó. Chúng ta có thể thiết lập như một bảng như thế này:
.commands
; Table of commands. Each command has the top bit
; of its last char. set. The table is terminated
; by a zero byte.
EQUS "ACCES"+CHR$(ASC"S"+&80)
EQUS "BACKU"+CHR$(ASC"P"+&80)
EQUS "COMPAC"+CHR$(ASC"T"+&80)
....
....
EQUS "TITL"+CHR$(ASC"E"+&80)
EQUS "WIP"+CHR$(ASC"E"+&80)
;
EQUB 0
ALIGN
; End of command table
Lưu ý việc sử dụng các ALIGN chỉ thị ở cuối của bảng.
Các ví dụ được trình bày cho đến nay đã cho thấy bộ nhớ khởi tạo được thiết lập, đó là bộ nhớ chứa dữ liệu chỉ đọc để sử dụng bởi các chương trình. Nhu cầu không gian dự trữ cho bộ nhớ khởi tạo cũng phát sinh. Ví dụ, nói rằng một chương trình sử dụng một hệ điều hành thường xuyên để tải một tập tin. Thói quen này có thể đòi hỏi một khối tham số trong bộ nhớ, trong đó có chứa tất cả các dữ liệu cần thiết để thực hiện tải. Để dành chỗ cho các khối tham số, một chuỗi các EQUD s có thể được sử dụng:
.fileBlock
; This is the parameter block for the file load operation
;
.namePtr EQUD 0 ; Pointer to the filename
.loadAddr EQUD 0 ; The load address of the file
.execAddr EQUD 0 ; The execution address
.attributes EQUD 0 ; The file's attributes
;
;
.doLoad
; This routine sets up the file block and loads the file
; It assumes the name is on the stack
;
STR sp, namePtr ; Save the name pointer
MOV R0, #&1000 ; Get 4K for the file
SWI getMemory ; Returns pointer in R1
STR R1, loadAddr ; Save the address
; Get the address of the parameter block in R0
ADR R0, fileBlock
MOV R1, #loadCode ; Command code for SWI
SWI file ; Do the load
...
Các EQUD s để thiết lập các thông số khối không có toán hạng có ý nghĩa. Điều này là bởi vì họ chỉ được sử dụng để đặt bộ nhớ mà sau này sẽ được điền đầy bởi chương trình. Mỗi EQUD có một nhãn để địa chỉ của khu vực bốn byte reserved có thể được truy cập bằng tên.
Theo chỉ thị thiết lập là một số mã mà sẽ sử dụng như một khối tham số. Nó sử dụng một số giả thuyết SWI gọi để truy cập thói quen hệ điều hành, nhưng bạn có thể giả định rằng các cơ sở tương tự sẽ có sẵn trên bất kỳ hệ thống ARM bạn có khả năng sử dụng. Một số hướng dẫn có thể không phải là rất rõ ràng ở giai đoạn này, nhưng bạn có thể muốn trở lại và xem xét lại ví dụ khi đọc hai chương tiếp theo.
Các ADR hướng dẫn không phải là một mnemonic ARM, nhưng là một chỉ thị được sử dụng để tải một đăng ký với một địa chỉ. Các toán hạng đầu tiên là đăng ký, trong đó địa chỉ là được lưu trữ. Các toán hạng thứ hai là địa chỉ được nạp. Chỉ thị này được chuyển đổi thành một trong những hướng dẫn này:
ADD reg, pc, # addr- (P% + 8) hoặc
reg SUB, pc, # (P% + 8) -addr
Các hình thức đầu tiên được sử dụng nếu các địa chỉ là sau khi chỉ thị trong chương trình, và các hình thức thứ hai được sử dụng nếu các địa chỉ xuất hiện trước đó, như trong ví dụ trên. Ý tưởng sử dụng các chỉ thị là để có được địa chỉ yêu cầu một cách độc lập vị, tức là trong một cách mà sẽ làm việc không có vấn đề mà chương trình được thực hiện. Chúng ta nói nhiều về vị trí độc lập trong Chương Năm.
Để dành chỗ cho các mục lớn hơn từ gấp đôi, bạn có thể sử dụng EQUS . Các toán hạng phải là một chuỗi có chiều dài là số lượng yêu cầu của byte. Các BASIC STRING $ chức năng cung cấp một cách tốt để có được một chuỗi như vậy. Ví dụ dưới đây dành hai khu vực 32 byte:
.path1 EQUS STRING $ (32, CHR $ 0); 32 byte cho filename1
.path2 EQUS STRING $ (32, CHR $ 0); 32 byte cho filename2
Bằng cách làm cho các toán hạng thứ hai của STRING $ CHR $ 0 , các byte nhúng muốn nhờ ASCII 0. Vì đây là một nhân vật tên tập tin bất hợp pháp, nó có thể được sử dụng để bảo vệ chống lại các chương trình bỏ qua để khởi tạo các dây trước khi sử dụng chúng.
Trong mỗi ví dụ trên, các chỉ thị được sử dụng để đặt lưu trữ bên trong mã máy của chương trình hợp ngữ. Sử dụng khác là trong việc xác định nhãn như offsets từ khi bắt đầu của một số khu vực làm việc. Ví dụ, chúng ta có thể sử dụng đăng ký R10 là một con trỏ đến một khối nhớ nơi chúng tôi lưu trữ quan trọng, dữ liệu thường xuyên truy cập (nhưng mà không phù hợp trong sổ đăng ký). Các LDR nhóm các lệnh sẽ được sử dụng để truy cập dữ liệu, sử dụng hiệu ngay lập tức. Giả sử các cấu trúc dữ liệu là như thế này:
Offset
Item
Bytes
&00Program pointer4
&04Top of program4
&08Start of variables4
&12End of variables4
&16Character count4
&20ON ERROR pointer4
&24String length1
&25List option1
&26OPT value1
&27...1
Các mục dữ liệu là những thứ mà một thông dịch BASIC sẽ lưu trữ trong khu vực dữ liệu của nó. Chú ý rằng tất cả các mục từ có độ dài được lưu trữ trong một nhóm để họ vẫn có lời kết. Một cách để gán các hiệu số trong lắp ráp BBC BASIC sẽ với các bài tập:
page = 0
top = page+4
lomem = top+4
vartop = lomem+4
count = vartop+4
errptr = count+4
strlen = errptr+4
listo = strlen+1
opt = listo+1
....= opt+1
Phương pháp này có một vài hạn chế. Trước tiên, để tìm ra bao nhiêu byte đã được giao cho một mục nào đó, bạn phải nhìn vào hai dòng và thực hiện một phép trừ. Tương tự như vậy, để chèn một mục mới, hai dòng phải được sửa đổi. Thậm chí tệ hơn, các giá trị thực tế giao cho các nhãn có thể không được phát hiện, không in riêng cho họ - họ không xuất hiện trong danh sách lắp ráp.
Để khắc phục những hạn chế, chúng tôi sử dụng khả năng lắp ráp bù đắp của lắp ráp. Phương pháp này được minh họa dưới đây:
DIM org 1000 : REM Enough for code and data declarations FOR pass=0 TO 2 STEP 2
PROCdeclarations(org,pass)
PROCasm(org,pass)
NEXT
END
DEF PROCdeclarations(org,pass)
P%=0 : O%=org
[ OPT pass + 4
.page EQUD 0
.top EQUD 0
.lomem EQUD 0
.vartop EQUD 0
.count EQUD 0
.errptr EQUD 0
.strlen EQUB 0
.listo EQUB 0
.opt EQUB 0
... EQUB 0
]
ENDPROC
DEF PROCasm(org,pass)
P%=org
[ OPT pass
....
Thủ tục PROCdeclarations chứa EQU chỉ thị trong đó xác định không gian cho mỗi mục. Bằng cách thêm 4 đến vượt qua giá trị, lắp ráp bù đắp được sử dụng. Thiết P% bằng không có nghĩa là nhãn đầu tiên,trang , sẽ có giá trị bằng không; đầu sẽ được thiết lập đến 4, và như vậy. O% được thiết lập để org ban đầu để các byte được lắp ráp bởi các chỉ thị sẽ được lưu trữ trong cùng một vị trí như mã chương trình.Như PROCasm được gọi sau khi PROCdeclarations , mã sẽ ghi đè lên các byte.
Khi kỹ thuật này được sử dụng, địa chỉ của các nhãn xuất hiện trong danh sách lắp ráp (sử dụng cho đường chuyền = 0 TO 3 BƯỚC 3 ); nó là dễ dàng hơn để xem có bao nhiêu byte mỗi mục chiếm (bằng cách nhìn thấy mà chỉ được sử dụng), và thêm một mục mới chỉ đơn giản là một vấn đề chèn dòng thích hợp.
4.6 Macro và lắp ráp có điều kiện
Đôi khi, một chuỗi các hướng dẫn xuất hiện thường xuyên trong một chương trình. Một ví dụ là thiết lập một bảng. Giả sử chúng ta có một bảng trong đó bao gồm 32 mục, mỗi dòng chứa một lệnh rẽ nhánh và ba mục dữ liệu, một byte hai một và hai mục byte. Để thiết lập bảng, chúng ta có thể có một danh sách gồm 32 trình tự hướng dẫn, mỗi hình thức:
Địa chỉ B
EQUW data1
EQUB data2
EQUB data3
Gõ trong bốn dòng 32 lần có thể chứng minh một ít mất thời gian, và có thể dễ bị lỗi.
Nhiều người lắp ráp chuyên dụng cung cấp một cơ sở gọi là macro để làm cho cuộc sống một chút dễ dàng hơn trong những tình huống như mô tả ở trên. Một vĩ mô có thể được xem như là một khuôn mẫu được định nghĩa chỉ một lần cho chuỗi. Nó sau đó được 'gọi' sử dụng một hướng dẫn đơn dòng để tạo ra tất cả các hướng dẫn. BBC BASIC lắp ráp thực hiện macro theo một cách mới lạ, bằng cách sử dụng người dùng định nghĩa chức năng từ khóa, FN .
Nếu lắp ráp gặp một FN nơi nó là hy vọng để tìm thấy một ghi nhớ, chức năng được gọi là bình thường, nhưng bất kỳ kết quả nó trả được bỏ qua. Một khi bạn đang ở trong FN , bạn có toàn bộ sức mạnh của BASIC có sẵn. Điều này bao gồm lắp ráp, vì vậy bạn có thể bắt đầu để lắp ráp mã trong FN . Điều này sẽ được bổ sung vào mã máy đó đã được lắp ráp trong chương trình chính.
Để làm rõ tình hình, chúng tôi sẽ thực hiện một macro để làm cho một mục bảng của các loại được mô tả ở trên.
10000 DEF FNtable(address,data1,data2,data3)
10010 [ OPT pass
10020 B address
10030 EQUW data1
10040 EQUB data2
10050 EQUB data3
10060 ]
10070 =""
Như bạn có thể thấy, 'body' của vĩ mô là rất tương tự như danh sách các hướng dẫn đưa ra trước đó. Các vĩ mô được xác định trong một phần riêng biệt của chương trình, ngoài phần ngôn ngữ lắp ráp chính (vì số dòng rất cao). Nó giả định rằng các biến lặp được sử dụng để kiểm soát các đường chuyền của lắp ráp được gọi là vượt qua , nhưng điều này có thể đã được đưa ra như là một tham số phụ.
Để gọi các macro, một FNtable cuộc gọi được sử dụng, một cho mỗi mục trong bảng. Một chuỗi điển hình có thể giống như thế này:
1240 FNtable(list,1,2,3)
1250 FNtable(delete,2,3,4)
1260 FNtable(renumber,1,1,2)
1270 ....
Khi FN được đạt tới, lắp ráp gọi hàm sử dụng các thông số được cung cấp. Hiệu quả của việc này là để lắp ráp các hướng dẫn và sau đó quay trở lại. Giá trị trả về "" được bỏ qua. Hiệu ứng ròng của tất cả điều này là để lắp ráp bốn dòng mã với chỉ có một hướng dẫn.
Một sử dụng các macro là để 'phát minh' hướng dẫn mới. Ví dụ, thay vì luôn luôn phải nhớ mà loại chồng bạn đang sử dụng (đầy đủ, trống rỗng, tăng dần hoặc giảm dần), bạn có thể định nghĩa một macro mà đẩy một phạm vi nhất định của các thanh ghi. Bằng cách luôn luôn sử dụng các macro, bạn cũng tránh pitfuls của quên ! Để biểu thị ghi lại.
Những thói quen dưới đây là một vĩ mô để thúc đẩy một loạt các đăng ký vào một đầy đủ, giảm dần ngăn xếp, sử dụng R13.
10000 DEF FNpush(first,last)
10010 [ OPT pass
10020 STMFD R13!,{first-last}
10030 ]
10040 =""
Một lợi thế của việc sử dụng một macro để thực hiện một hoạt động phổ biến như push là nó có thể được thay đổi chỉ một lần để ảnh hưởng đến tất cả những nơi mà nó được sử dụng. Giả sử, ví dụ, rằng bạn phải thay đổi chương trình của bạn để sử dụng một ngăn xếp rỗng, vì vậy mà nó tương thích với các chương trình của người khác. Bởi đơn giản là thay đổi dòng 10020 trên, mọi hoạt động đẩy sẽ sử dụng các loại mới của chồng.
Có những tình huống mà bạn có thể muốn để có thể lắp ráp các phiên bản khác nhau của một chương trình, tùy thuộc vào một số điều kiện. Đây là nơi lắp ráp có điều kiện đến.
Sử dụng lắp ráp có điều kiện là một chút như IFE loại hoạt động trong BASIC, và đó chính xác là những gì chúng tôi sử dụng để thực hiện nó trong lắp ráp BASIC BBC. Các trang sau niêm yết cho thấy cách vĩ mô FNpush có thể được viết để đối phó với một trong hai một chồng đầy hoặc rỗng.
Biến fullStack là một toàn cầu được xác định vào lúc bắt đầu của chương trình để TRUE nếu chúng được lắp ráp cho một ngăn xếp đầy đủ và FALSE nếu chúng ta muốn một một sản phẩm nào.
10000 DEF FNpush(first,last)
10010 IF fullStack THEN
10020 [ OPT pass
10030 STMFD R13!,{first,last}
10040 ]
10050 ELSE
10060 [ OPT pass
10070 STMED R13!,{first,last}
10080 ]
10090 ENDIF
10100 =""
Hầu hết các tính năng của các nhà lắp ráp chuyên dụng có thể được thực hiện bằng cách sử dụng sự kết hợp của BBC BASIC và lắp ráp BASIC. Ví dụ, một số nhà lắp ráp có một KHI vòng chỉ có thể được sử dụng để lắp ráp các hướng dẫn lặp đi lặp lại mà sẽ được tẻ nhạt (và có thể dễ bị lỗi) để gõ. Các BASIC WHILE (hoặc CHO hoặc REPEAT ) loops có thể được sử dụng để hiệu ứng tương tự. Các vĩ mô bên dưới thực hiện một 'điền vào bộ nhớ' chức năng. Phải mất một số lượng và giá trị byte, và lưu giữ số các byte trong mã đối tượng.
10000 DEF FNfill(byte,count)
10010 IF count=0 THEN =""
10020 LOCAL i
10030 FOR i=1 TO count
10040 [ OPT pass
10050 EQUB byte
10060 ]
10070 NEXT i
10080 [ OPT pass
10090 ALIGN
10100 ]
10110 =""
Mặc dù chúng ta có thể khá dễ dàng 'chọc' các byte vào mã mà không sử dụng EQUB , phương pháp chúng tôi sử dụng đảm bảo rằng tác động của FNfill hiển thị trên danh sách lắp ráp. Điều quan trọng để giữ những gì được in trong danh sách phù hợp với những gì đang thực sự xảy ra là, nếu không nó trở nên khó khăn cho các chương trình gỡ lỗi từ danh sách.
4.7 Gọi mã máy
Nhiều người trong số các chương trình ví dụ trong hai chương tiếp theo được đi kèm bởi thói quen kiểm tra ngắn. Chúng được gọi là từ BASIC sau khi mã đã được lắp ráp. Để giúp bạn hiểu các chương trình này, chúng tôi mô tả các CALL tuyên bố và USR chức năng trong phần này.
Định dạng của CALL tuyên bố là:
CALL <địa chỉ>, [<tham số>]
Các <address> là vị trí của mã máy mà bạn muốn thực hiện. Các tùy chọn <thông số> là các biến BASIC, cách nhau bởi dấu phẩy.
Khi được gọi là bắt đầu thường xuyên để thực hiện, nó tìm thấy sổ của nó thiết lập bởi BASIC. Chúng tôi sẽ không mô tả được toàn bộ những gì BASIC cung cấp cho các chương trình mã máy, nhưng thay vì chỉ ra những tính năng hữu ích nhất. Đầu tiên, R0 đến R7 là thiết lập từ các giá trị của tám trong số các biến hệ thống số nguyên (A% vv). Vì vậy, R0 giữ nội dung của A%, R1 được khởi tạo từ B%, và như vậy, đến R7 đã được nạp từ H%.
R9 chỉ vào danh sách các thông số, và R10 có chứa số lượng các thông số tiếp theo sau <address> . Điều này có thể được không, như các thông số là tùy chọn. Các dữ liệu mà R9 điểm là một danh sách các cặp từ. Có một từ đôi cho mỗi tham số.
Từ đầu tiên chứa địa chỉ của biến, tức là nơi mà giá trị của nó được lưu trữ trong bộ nhớ. Từ thứ hai giữ kiểu của biến. Từ điều này, ý nghĩa của số liệu quyết bằng các từ đầu tiên có thể được xác định. Các loại chính như sau:
Kiểu
Ý nghĩa
0Độc byte, ví dụ ? Addr
4Số nguyên, ví dụ như fred% , ! Fred , fred% (10)
5Real, ví dụ như fred , | fred , fred (10)
128String, ví dụ như fred $ , fred $ (10)
129String, ví dụ như $ jerry
Loại-128 giá trị chuỗi bao gồm năm byte. Bốn đầu tiên là địa chỉ của các nội dung của chuỗi (ví dụ như các nhân vật), và byte thứ năm là chiều dài của chuỗi. Một kiểu chuỗi-129 là một chuỗi các byte chấm dứt bởi một nhân vật vận chuyển một chiều và các điểm địa chỉ từ vào ký tự đầu tiên của chuỗi. Lưu ý rằng các giá trị dữ liệu có thể có bất kỳ sự liên kết, do đó, để truy cập chúng bạn phải sử dụng một thói quen chung kết của các loại được trình bày trong Chương Sáu.
Điều quan trọng là nhận ra rằng từ cặp mô tả các thông số theo thứ tự ngược lại. Điều này có nghĩa rằng các từ được trỏ đến bởi R9 là địa chỉ của các cuối cùng giá trị tham số của. Từ tiếp theo là kiểu của các tham số cuối cùng, và một sau đó là địa chỉ của tham số áp chót, và như vậy. Rõ ràng là nếu chỉ có một tham số, rerversal này không quan trọng.
Việc đăng ký cuối cùng của tầm quan trọng là R13, đó là con trỏ ngăn xếp BASIC, và R14, liên kết đăng ký. (Đầy đủ, giảm dần) stack BASIC có thể được dùng bởi các thói quen gọi là, miễn là con trỏ ngăn xếp kết thúc với cùng một giá trị nó đã có trên nhập cảnh vào các thói quen. R14 có chứa địa chỉ trở lại, nếu như các thói quen đã được gọi với một BL hướng dẫn. Điều này cho phép các thói quen để trở về BASIC với một lệnh chẳng hạn như:
MOV pc, R14
Các USR chức năng được sử dụng trong một cách tương tự như CALL . Có hai khác biệt quan trọng. Đầu tiên, nó không thể lấy các thông số, vì vậy việc sử dụng điển hình sẽ là:
IN ~ địa chỉ USR
Thứ hai, nó sẽ trả về một kết quả. Kết quả này là nội dung của R0 trên đường trở về từ các mã máy. Có một số ví dụ về USR trong hai chương tiếp theo.
4.8 Tóm tắt
Trong chương này, chúng tôi đã xem xét một trong những nhà lắp ráp sẵn để các lập trình ARM. Bởi vì nó là một phần của ARM BASIC, nó là đơn giản để sử dụng. Mặc dù các nhà lắp ráp tự nó không tràn với các tính năng, thực tế là lắp ráp và BASIC có thể được kết hợp một cách tự do có nhiều lợi thế. Nó là tương đối dễ dàng để thực hiện rất nhiều các tính năng của các nhà lắp ráp 'chuyên nghiệp', chẳng hạn như các macro và lắp ráp có điều kiện.

Cuối cùng chúng tôi cũng xem xét các CALL và USR từ khóa trong BASIC, để chuẩn bị cho việc sử dụng chúng trong các chương sau.

0 Comment:

Đăng nhận xét

Thank you for your comments!